kdirgul commited on
Commit
3d07ab6
·
verified ·
1 Parent(s): 56ecbe6

A vendor source

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +3 -0
  2. vendor/mamba-og/.gitignore +6 -0
  3. vendor/mamba-og/.gitmodules +3 -0
  4. vendor/mamba-og/AUTHORS +2 -0
  5. vendor/mamba-og/LICENSE +201 -0
  6. vendor/mamba-og/MANIFEST.in +3 -0
  7. vendor/mamba-og/README.md +282 -0
  8. vendor/mamba-og/assets/mamba3.png +3 -0
  9. vendor/mamba-og/assets/selection.png +3 -0
  10. vendor/mamba-og/assets/ssd_algorithm.png +3 -0
  11. vendor/mamba-og/benchmarks/benchmark_generation_mamba_simple.py +92 -0
  12. vendor/mamba-og/csrc/selective_scan/reverse_scan.cuh +452 -0
  13. vendor/mamba-og/csrc/selective_scan/selective_scan.cpp +497 -0
  14. vendor/mamba-og/csrc/selective_scan/selective_scan.h +82 -0
  15. vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_bf16_complex.cu +9 -0
  16. vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_bf16_real.cu +9 -0
  17. vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_fp16_complex.cu +9 -0
  18. vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_fp16_real.cu +9 -0
  19. vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_fp32_complex.cu +9 -0
  20. vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_fp32_real.cu +9 -0
  21. vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_kernel.cuh +561 -0
  22. vendor/mamba-og/csrc/selective_scan/selective_scan_common.h +255 -0
  23. vendor/mamba-og/csrc/selective_scan/selective_scan_fwd_bf16.cu +10 -0
  24. vendor/mamba-og/csrc/selective_scan/selective_scan_fwd_fp16.cu +10 -0
  25. vendor/mamba-og/csrc/selective_scan/selective_scan_fwd_fp32.cu +10 -0
  26. vendor/mamba-og/csrc/selective_scan/selective_scan_fwd_kernel.cuh +376 -0
  27. vendor/mamba-og/csrc/selective_scan/static_switch.h +25 -0
  28. vendor/mamba-og/csrc/selective_scan/uninitialized_copy.cuh +77 -0
  29. vendor/mamba-og/evals/lm_harness_eval.py +39 -0
  30. vendor/mamba-og/mamba_ssm/__init__.py +24 -0
  31. vendor/mamba-og/mamba_ssm/distributed/__init__.py +0 -0
  32. vendor/mamba-og/mamba_ssm/distributed/distributed_utils.py +144 -0
  33. vendor/mamba-og/mamba_ssm/distributed/tensor_parallel.py +296 -0
  34. vendor/mamba-og/mamba_ssm/models/__init__.py +0 -0
  35. vendor/mamba-og/mamba_ssm/models/config_mamba.py +18 -0
  36. vendor/mamba-og/mamba_ssm/models/mixer_seq_simple.py +309 -0
  37. vendor/mamba-og/mamba_ssm/modules/__init__.py +0 -0
  38. vendor/mamba-og/mamba_ssm/modules/block.py +91 -0
  39. vendor/mamba-og/mamba_ssm/modules/mamba2.py +383 -0
  40. vendor/mamba-og/mamba_ssm/modules/mamba2_simple.py +200 -0
  41. vendor/mamba-og/mamba_ssm/modules/mamba3.py +498 -0
  42. vendor/mamba-og/mamba_ssm/modules/mamba_simple.py +294 -0
  43. vendor/mamba-og/mamba_ssm/modules/mha.py +294 -0
  44. vendor/mamba-og/mamba_ssm/modules/mlp.py +34 -0
  45. vendor/mamba-og/mamba_ssm/modules/ssd_minimal.py +103 -0
  46. vendor/mamba-og/mamba_ssm/ops/__init__.py +0 -0
  47. vendor/mamba-og/mamba_ssm/ops/cute/__init__.py +0 -0
  48. vendor/mamba-og/mamba_ssm/ops/cute/mamba3/__init__.py +0 -0
  49. vendor/mamba-og/mamba_ssm/ops/cute/mamba3/mamba3_step_fn.py +933 -0
  50. vendor/mamba-og/mamba_ssm/ops/selective_scan_interface.py +419 -0
.gitattributes CHANGED
@@ -35,3 +35,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  wheels/causal_conv1d-1.6.2.post1-cp312-cp312-linux_x86_64.whl filter=lfs diff=lfs merge=lfs -text
37
  wheels/mamba_ssm-2.3.2.post1-cp312-cp312-linux_x86_64.whl filter=lfs diff=lfs merge=lfs -text
 
 
 
 
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  wheels/causal_conv1d-1.6.2.post1-cp312-cp312-linux_x86_64.whl filter=lfs diff=lfs merge=lfs -text
37
  wheels/mamba_ssm-2.3.2.post1-cp312-cp312-linux_x86_64.whl filter=lfs diff=lfs merge=lfs -text
38
+ vendor/mamba-og/assets/mamba3.png filter=lfs diff=lfs merge=lfs -text
39
+ vendor/mamba-og/assets/selection.png filter=lfs diff=lfs merge=lfs -text
40
+ vendor/mamba-og/assets/ssd_algorithm.png filter=lfs diff=lfs merge=lfs -text
vendor/mamba-og/.gitignore ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ *__pycache__/
2
+ *.egg-info/
3
+ build/
4
+ **.so
5
+ *.hip
6
+ *_hip.*
vendor/mamba-og/.gitmodules ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ [submodule "3rdparty/lm-evaluation-harness"]
2
+ path = 3rdparty/lm-evaluation-harness
3
+ url = https://github.com/EleutherAI/lm-evaluation-harness/
vendor/mamba-og/AUTHORS ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ Tri Dao, tri@tridao.me
2
+ Albert Gu, agu@andrew.cmu.edu
vendor/mamba-og/LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2023 Tri Dao, Albert Gu
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
vendor/mamba-og/MANIFEST.in ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ recursive-include csrc *
2
+ recursive-include csrc *
3
+ README.md
vendor/mamba-og/README.md ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Mamba
2
+
3
+ ![Mamba](assets/selection.png "Selective State Space")
4
+ > **Mamba: Linear-Time Sequence Modeling with Selective State Spaces**\
5
+ > Albert Gu*, Tri Dao*\
6
+ > Paper: https://arxiv.org/abs/2312.00752
7
+
8
+ ![Mamba-2](assets/ssd_algorithm.png "State Space Dual Model")
9
+ > **Transformers are SSMs: Generalized Models and Efficient Algorithms**\
10
+ > **Through Structured State Space Duality**\
11
+ > Tri Dao*, Albert Gu*\
12
+ > Paper: https://arxiv.org/abs/2405.21060
13
+
14
+ ![Mamba-3](assets/mamba3.png "Inference-first State Space Model")
15
+ > **Mamba-3: Improved Sequence Modeling using State Space Principles**\
16
+ > **Through Structured State Space Duality**\
17
+ > Aakash Lahoti*, Kevin Y. Li*, Berlin Chen*, Caitlin Wang*, Aviv Bick, J. Zico Kolter, Tri Dao†, Albert Gu†\
18
+ > Paper: https://arxiv.org/abs/2603.15569
19
+
20
+ ## About
21
+
22
+ Mamba is a new state space model architecture showing promising performance on information-dense data such as language modeling, where previous subquadratic models fall short of Transformers.
23
+ It is based on the line of progress on [structured state space models](https://github.com/state-spaces/s4),
24
+ with an efficient hardware-aware design and implementation in the spirit of [FlashAttention](https://github.com/Dao-AILab/flash-attention).
25
+
26
+ ## Installation
27
+
28
+ Install PyTorch first, then:
29
+ - [Option] `pip install causal-conv1d>=1.4.0 --no-build-isolation`: an efficient implementation of a simple causal Conv1d layer used inside the Mamba block.
30
+ - `pip install mamba-ssm --no-build-isolation`: the core Mamba package.
31
+ - `pip install mamba-ssm[causal-conv1d] --no-build-isolation`: To install core Mamba package and causal-conv1d.
32
+
33
+ `--no-build-isolation` is required so that pip uses your existing CUDA-enabled PyTorch instead of installing torch-cpu in an isolated build environment.
34
+
35
+ NOTE: To use Mamba-3, please install from source `MAMBA_FORCE_BUILD=TRUE pip install --no-cache-dir --force-reinstall git+https://github.com/state-spaces/mamba.git --no-build-isolation`.
36
+
37
+ Other requirements:
38
+ - Linux
39
+ - NVIDIA GPU
40
+ - PyTorch 1.12+
41
+ - CUDA 11.6+
42
+
43
+ For AMD cards, see additional prerequisites below.
44
+
45
+ ## Usage
46
+
47
+ We expose several levels of interface with the Mamba model.
48
+
49
+ ### Selective SSM
50
+
51
+ Mamba is based on a selective SSM layer, which is the focus of the paper (Section 3; Algorithm 2).
52
+
53
+ Source: [ops/selective_scan_interface.py](mamba_ssm/ops/selective_scan_interface.py).
54
+
55
+ ### Mamba Block
56
+
57
+ The main module of this repository is the Mamba architecture block wrapping the selective SSM.
58
+
59
+ Source: [modules/mamba_simple.py](mamba_ssm/modules/mamba_simple.py).
60
+
61
+ Usage:
62
+ ``` python
63
+ import torch
64
+ from mamba_ssm import Mamba
65
+
66
+ batch, length, dim = 2, 64, 16
67
+ x = torch.randn(batch, length, dim).to("cuda")
68
+ model = Mamba(
69
+ # This module uses roughly 3 * expand * d_model^2 parameters
70
+ d_model=dim, # Model dimension d_model
71
+ d_state=16, # SSM state expansion factor
72
+ d_conv=4, # Local convolution width
73
+ expand=2, # Block expansion factor
74
+ ).to("cuda")
75
+ y = model(x)
76
+ assert y.shape == x.shape
77
+ ```
78
+
79
+ ### Mamba-2
80
+
81
+ The Mamba-2 block is implemented at [modules/mamba2.py](mamba_ssm/modules/mamba2.py).
82
+
83
+ A simpler version is at [modules/mamba2_simple.py](mamba_ssm/modules/mamba2_simple.py)
84
+
85
+ The usage is similar to Mamba(-1):
86
+ ``` python
87
+ from mamba_ssm import Mamba2
88
+ model = Mamba2(
89
+ # This module uses roughly 3 * expand * d_model^2 parameters
90
+ d_model=dim, # Model dimension d_model
91
+ d_state=64, # SSM state expansion factor, typically 64 or 128
92
+ d_conv=4, # Local convolution width
93
+ expand=2, # Block expansion factor
94
+ ).to("cuda")
95
+ y = model(x)
96
+ assert y.shape == x.shape
97
+ ```
98
+
99
+ #### SSD
100
+
101
+ A minimal version of the inner SSD module (Listing 1 from the Mamba-2 paper) with conversion between "discrete" and "continuous" SSM versions
102
+ is at [modules/ssd_minimal.py](mamba_ssm/modules/ssd_minimal.py).
103
+
104
+ ### Mamba-3
105
+
106
+ The Mamba-3 block is implemented at [modules/mamba3.py](mamba_ssm/modules/mamba3.py).
107
+
108
+ The usage is as follows:
109
+ ``` python
110
+ from mamba_ssm import Mamba3
111
+ batch, length, dim = 2, 2048, 768
112
+ x = torch.randn(batch, length, dim).to(torch.bfloat16).to("cuda")
113
+ model = Mamba3(
114
+ # This module uses roughly 6 * d_model^2 parameters
115
+ d_model=dim, # Model dimension d_model
116
+ d_state=128, # SSM state size
117
+ headdim=64, # SSM headdim
118
+ is_mimo=True, # Use MIMO mode
119
+ mimo_rank=4, # MIMO rank when is_mimo=True
120
+ chunk_size=16, # 64/mimo_rank if x is in bf16, else 32/mimo_rank
121
+ is_outproj_norm=False, # Additional post SSM norm
122
+ dtype=torch.bfloat16,
123
+ ).to("cuda")
124
+ y = model(x)
125
+ assert y.shape == x.shape
126
+ ```
127
+
128
+ ### Mamba Language Model
129
+
130
+ Finally, we provide an example of a complete language model: a deep sequence model backbone (with repeating Mamba blocks) + language model head.
131
+
132
+ Source: [models/mixer_seq_simple.py](mamba_ssm/models/mixer_seq_simple.py).
133
+
134
+ This is an example of how to integrate Mamba into an end-to-end neural network.
135
+ This example is used in the generation scripts below.
136
+
137
+
138
+ ## Pretrained Models
139
+
140
+ Pretrained models are uploaded to
141
+ [Hugging Face](https://huggingface.co/state-spaces): `mamba-130m`, `mamba-370m`,
142
+ `mamba-790m`, `mamba-1.4b`, `mamba-2.8b`, `mamba2-130m`, `mamba2-370m`,
143
+ `mamba2-780m`, `mamba2-1.3b`, `mamba2-2.7b`, `transformerpp-2.7b`, `mamba2attn-2.7b`, trained on 300B tokens on the Pile, as well as `mamba-2.8b-slimpj`
144
+ (trained on 600B tokens on the SlimPajama dataset).
145
+
146
+
147
+ The models will be autodownloaded by the generation script below.
148
+
149
+ These models were trained on the [Pile](https://huggingface.co/datasets/EleutherAI/pile), and follow the standard model dimensions described by GPT-3 and followed by many open source models:
150
+
151
+ | Parameters | Layers | Model dim. |
152
+ |------------|--------|------------|
153
+ | 130M | 24 | 768 |
154
+ | 370M | 48 | 1024 |
155
+ | 790M | 48 | 1536 |
156
+ | 1.4B | 48 | 2048 |
157
+ | 2.8B | 64 | 2560 |
158
+
159
+ (The layer count of Mamba doubles that of a Transformer with similar size, as two Mamba blocks are needed for each "layer" (MHA block + MLP block) of a Transformer.)
160
+
161
+ Note: these are base models trained only for 300B tokens, without any form of downstream modification (instruction tuning, etc.).
162
+ Performance is expected to be comparable or better than other architectures trained on similar data, but not to match larger or fine-tuned models.
163
+
164
+
165
+ ## Evaluations
166
+
167
+ To run zero-shot evaluations of models (corresponding to Table 3 of the paper),
168
+ we use the
169
+ [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness)
170
+ library.
171
+
172
+ 1. Install `lm-evaluation-harness` by `pip install lm-eval==0.4.2`.
173
+ 2. Run evaluation with (more documentation at the [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness/tree/big-refactor) repo):
174
+ ``` sh
175
+ lm_eval --model mamba_ssm --model_args pretrained=state-spaces/mamba-130m --tasks lambada_openai,hellaswag,piqa,arc_easy,arc_challenge,winogrande,openbookqa --device cuda --batch_size 256
176
+ python evals/lm_harness_eval.py --model hf --model_args pretrained=EleutherAI/pythia-160m --tasks lambada_openai,hellaswag,piqa,arc_easy,arc_challenge,winogrande --device cuda --batch_size 64
177
+ ```
178
+
179
+ To reproduce the results on the `mamba-2.8b-slimpj` model reported in the blogposts:
180
+ ``` sh
181
+ lm_eval --model mamba_ssm --model_args pretrained=state-spaces/mamba-2.8b-slimpj --tasks boolq,piqa,hellaswag,winogrande,arc_easy,arc_challenge,openbookqa,race,truthfulqa_mc2 --device cuda --batch_size 256
182
+ lm_eval --model mamba_ssm --model_args pretrained=state-spaces/mamba-2.8b-slimpj --tasks mmlu --num_fewshot 5 --device cuda --batch_size 256
183
+ ```
184
+
185
+ To run evaluations on Mamba-2 models, simply replace the model names:
186
+ ``` sh
187
+ lm_eval --model mamba_ssm --model_args pretrained=state-spaces/mamba2-2.7b --tasks lambada_openai,hellaswag,piqa,arc_easy,arc_challenge,winogrande,openbookqa --device cuda --batch_size 256
188
+ lm_eval --model mamba_ssm --model_args pretrained=state-spaces/transformerpp-2.7b --tasks lambada_openai,hellaswag,piqa,arc_easy,arc_challenge,winogrande,openbookqa --device cuda --batch_size 256
189
+ lm_eval --model mamba_ssm --model_args pretrained=state-spaces/mamba2attn-2.7b --tasks lambada_openai,hellaswag,piqa,arc_easy,arc_challenge,winogrande,openbookqa --device cuda --batch_size 256
190
+ ```
191
+
192
+ Note that the result of each task might differ from reported values by 0.1-0.3 due to noise in the evaluation process.
193
+
194
+ ## Inference
195
+
196
+ The script [benchmarks/benchmark_generation_mamba_simple.py](benchmarks/benchmark_generation_mamba_simple.py)
197
+ 1. autoloads a model from the Hugging Face Hub,
198
+ 2. generates completions of a user-specified prompt,
199
+ 3. benchmarks the inference speed of this generation.
200
+
201
+ Other configurable options include the top-p (nucleus sampling) probability, and the softmax temperature.
202
+
203
+ ### Examples
204
+
205
+ To test generation latency (e.g. batch size = 1) with different sampling strategies:
206
+
207
+ ``` sh
208
+ python benchmarks/benchmark_generation_mamba_simple.py --model-name "state-spaces/mamba-2.8b" --prompt "My cat wrote all this CUDA code for a new language model and" --topp 0.9 --temperature 0.7 --repetition-penalty 1.2
209
+ python benchmarks/benchmark_generation_mamba_simple.py --model-name "EleutherAI/pythia-2.8b" --prompt "My cat wrote all this CUDA code for a new language model and" --topp 0.9 --temperature 0.7 --repetition-penalty 1.2
210
+ python benchmarks/benchmark_generation_mamba_simple.py --model-name "state-spaces/mamba-2.8b" --prompt "My cat wrote all this CUDA code for a new language model and" --minp 0.05 --topk 0 --temperature 0.7 --repetition-penalty 1.2
211
+ ```
212
+
213
+ To test generation throughput with random prompts (e.g. large batch size):
214
+ ``` sh
215
+ python benchmarks/benchmark_generation_mamba_simple.py --model-name "state-spaces/mamba-2.8b" --batch 64
216
+ python benchmarks/benchmark_generation_mamba_simple.py --model-name "EleutherAI/pythia-2.8b" --batch 64
217
+ ```
218
+
219
+ With Mamba-2, you just need to change the model name:
220
+ ``` sh
221
+ python benchmarks/benchmark_generation_mamba_simple.py --model-name "state-spaces/mamba2-2.7b" --prompt "My cat wrote all this CUDA code for a new language model and" --topp 0.9 --temperature 0.7 --repetition-penalty 1.2
222
+ ```
223
+
224
+
225
+ ## Troubleshooting
226
+
227
+ ### Precision
228
+ Our models were trained using PyTorch [AMP](https://pytorch.org/docs/stable/amp.html) for mixed precision. AMP keeps model parameters in float32 and casts to half precision when necessary.
229
+ On the other hand, other frameworks like DeepSpeed store parameters in float16 and upcasts when necessary (e.g. for optimizer accumulation).
230
+
231
+ We've observed that higher precision for the main model parameters may be necessary, because SSMs are sensitive to their recurrent dynamics. If you are experiencing instabilities,
232
+ as a first step please try a framework storing parameters in fp32 (such as AMP).
233
+
234
+ ### Initialization
235
+ Some parts of the model have initializations inherited from prior work on S4 models.
236
+ For [example](https://github.com/state-spaces/mamba/blob/f0affcf69f06d1d06cef018ff640bf080a11c421/mamba_ssm/modules/mamba_simple.py#L102), the $\Delta$ parameter has a targeted range by initializing the bias of its linear projection.
237
+ However, some frameworks may have post-initialization hooks (e.g. setting all bias terms in `nn.Linear` modules to zero).
238
+ If this is the case, you may have to add custom logic (e.g. this [line](https://github.com/state-spaces/mamba/blob/f0affcf69f06d1d06cef018ff640bf080a11c421/mamba_ssm/modules/mamba_simple.py#L104) turns off re-initializing in our trainer, but would be a no-op in any other framework)
239
+ that is specific to the training framework.
240
+
241
+ ## Additional Prerequisites for AMD cards
242
+
243
+ ### Patching ROCm
244
+
245
+ If you are on ROCm 6.0, run the following steps to avoid errors during compilation. This is not required for ROCm 6.1 onwards.
246
+
247
+ 1. Locate your ROCm installation directory. This is typically found at `/opt/rocm/`, but may vary depending on your installation.
248
+
249
+ 2. Apply the Patch. Run with `sudo` in case you encounter permission issues.
250
+ ```bash
251
+ patch /opt/rocm/include/hip/amd_detail/amd_hip_bf16.h < rocm_patch/rocm6_0.patch
252
+ ```
253
+
254
+
255
+ ## Citation
256
+
257
+ If you use this codebase, or otherwise find our work valuable, please cite Mamba:
258
+ ```
259
+ @article{mamba,
260
+ title={Mamba: Linear-Time Sequence Modeling with Selective State Spaces},
261
+ author={Gu, Albert and Dao, Tri},
262
+ journal={arXiv preprint arXiv:2312.00752},
263
+ year={2023}
264
+ }
265
+
266
+ @inproceedings{mamba2,
267
+ title={Transformers are {SSM}s: Generalized Models and Efficient Algorithms Through Structured State Space Duality},
268
+ author={Dao, Tri and Gu, Albert},
269
+ booktitle={International Conference on Machine Learning (ICML)},
270
+ year={2024}
271
+ }
272
+
273
+ @misc{lahoti2026mamba3improvedsequencemodeling,
274
+ title={Mamba-3: Improved Sequence Modeling using State Space Principles},
275
+ author={Aakash Lahoti and Kevin Y. Li and Berlin Chen and Caitlin Wang and Aviv Bick and J. Zico Kolter and Tri Dao and Albert Gu},
276
+ year={2026},
277
+ eprint={2603.15569},
278
+ archivePrefix={arXiv},
279
+ primaryClass={cs.LG},
280
+ url={https://arxiv.org/abs/2603.15569},
281
+ }
282
+ ```
vendor/mamba-og/assets/mamba3.png ADDED

Git LFS Details

  • SHA256: 76088a9c16496358c9ffd09f0eb1458d343d92c40849f4763b14a96eed350567
  • Pointer size: 131 Bytes
  • Size of remote file: 181 kB
vendor/mamba-og/assets/selection.png ADDED

Git LFS Details

  • SHA256: d57aeeeca3250d0551956494aa0ef7f56f5758563d849cb3a3576f836e13b914
  • Pointer size: 131 Bytes
  • Size of remote file: 819 kB
vendor/mamba-og/assets/ssd_algorithm.png ADDED

Git LFS Details

  • SHA256: 91ab82b330761250c3241e4f16fed54a35081115c26777a4cc087c2f6e47f466
  • Pointer size: 132 Bytes
  • Size of remote file: 1.12 MB
vendor/mamba-og/benchmarks/benchmark_generation_mamba_simple.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023, Tri Dao, Albert Gu.
2
+
3
+ import argparse
4
+ import time
5
+ import json
6
+
7
+ import torch
8
+ import torch.nn.functional as F
9
+
10
+ from einops import rearrange
11
+
12
+ from transformers import AutoTokenizer, AutoModelForCausalLM
13
+
14
+ from mamba_ssm.models.mixer_seq_simple import MambaLMHeadModel
15
+
16
+
17
+ parser = argparse.ArgumentParser(description="Generation benchmarking")
18
+ parser.add_argument("--model-name", type=str, default="state-spaces/mamba-130m")
19
+ parser.add_argument("--prompt", type=str, default=None)
20
+ parser.add_argument("--promptlen", type=int, default=100)
21
+ parser.add_argument("--genlen", type=int, default=100)
22
+ parser.add_argument("--temperature", type=float, default=1.0)
23
+ parser.add_argument("--topk", type=int, default=1)
24
+ parser.add_argument("--topp", type=float, default=1.0)
25
+ parser.add_argument("--minp", type=float, default=0.0)
26
+ parser.add_argument("--repetition-penalty", type=float, default=1.0)
27
+ parser.add_argument("--batch", type=int, default=1)
28
+ args = parser.parse_args()
29
+
30
+ repeats = 3
31
+ device = "cuda"
32
+ dtype = torch.float16
33
+
34
+ print(f"Loading model {args.model_name}")
35
+ is_mamba = args.model_name.startswith("state-spaces/mamba") or args.model_name.startswith("state-spaces/transformerpp")
36
+ if is_mamba:
37
+ tokenizer = AutoTokenizer.from_pretrained("EleutherAI/gpt-neox-20b")
38
+ model = MambaLMHeadModel.from_pretrained(args.model_name, device=device, dtype=dtype)
39
+ else:
40
+ tokenizer = AutoTokenizer.from_pretrained(args.model_name)
41
+ model = AutoModelForCausalLM.from_pretrained(args.model_name, device_map={"": device}, torch_dtype=dtype)
42
+ model.eval()
43
+ print(f"Number of parameters: {sum(p.numel() for p in model.parameters() if p.requires_grad)}")
44
+
45
+ torch.random.manual_seed(0)
46
+ if args.prompt is None:
47
+ input_ids = torch.randint(1, 1000, (args.batch, args.promptlen), dtype=torch.long, device="cuda")
48
+ attn_mask = torch.ones_like(input_ids, dtype=torch.long, device="cuda")
49
+ else:
50
+ tokens = tokenizer(args.prompt, return_tensors="pt")
51
+ input_ids = tokens.input_ids.to(device=device)
52
+ attn_mask = tokens.attention_mask.to(device=device)
53
+ max_length = input_ids.shape[1] + args.genlen
54
+
55
+ if is_mamba:
56
+ fn = lambda: model.generate(
57
+ input_ids=input_ids,
58
+ max_length=max_length,
59
+ cg=True,
60
+ return_dict_in_generate=True,
61
+ output_scores=True,
62
+ enable_timing=False,
63
+ temperature=args.temperature,
64
+ top_k=args.topk,
65
+ top_p=args.topp,
66
+ min_p=args.minp,
67
+ repetition_penalty=args.repetition_penalty,
68
+ )
69
+ else:
70
+ fn = lambda: model.generate(
71
+ input_ids=input_ids,
72
+ attention_mask=attn_mask,
73
+ max_length=max_length,
74
+ return_dict_in_generate=True,
75
+ pad_token_id=tokenizer.eos_token_id,
76
+ do_sample=True,
77
+ temperature=args.temperature,
78
+ top_k=args.topk,
79
+ top_p=args.topp,
80
+ repetition_penalty=args.repetition_penalty,
81
+ )
82
+ out = fn()
83
+ if args.prompt is not None:
84
+ print(tokenizer.batch_decode(out.sequences.tolist()))
85
+
86
+ torch.cuda.synchronize()
87
+ start = time.time()
88
+ for _ in range(repeats):
89
+ fn()
90
+ torch.cuda.synchronize()
91
+ print(f"Prompt length: {len(input_ids[0])}, generation length: {len(out.sequences[0]) - len(input_ids[0])}")
92
+ print(f"{args.model_name} prompt processing + decoding time: {(time.time() - start) / repeats * 1000:.0f}ms")
vendor/mamba-og/csrc/selective_scan/reverse_scan.cuh ADDED
@@ -0,0 +1,452 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ #pragma once
6
+
7
+ #ifndef USE_ROCM
8
+ #include <cub/config.cuh>
9
+
10
+ #include <cub/util_ptx.cuh>
11
+ #include <cub/util_type.cuh>
12
+ #include <cub/block/block_raking_layout.cuh>
13
+ // #include <cub/detail/uninitialized_copy.cuh>
14
+ #else
15
+ #include <hipcub/hipcub.hpp>
16
+ namespace cub = hipcub;
17
+ #endif
18
+ #include "uninitialized_copy.cuh"
19
+
20
+ /**
21
+ * Perform a reverse sequential reduction over \p LENGTH elements of the \p input array. The aggregate is returned.
22
+ */
23
+ template <
24
+ int LENGTH,
25
+ typename T,
26
+ typename ReductionOp>
27
+ __device__ __forceinline__ T ThreadReverseReduce(const T (&input)[LENGTH], ReductionOp reduction_op) {
28
+ static_assert(LENGTH > 0);
29
+ T retval = input[LENGTH - 1];
30
+ #pragma unroll
31
+ for (int i = LENGTH - 2; i >= 0; --i) { retval = reduction_op(retval, input[i]); }
32
+ return retval;
33
+ }
34
+
35
+ /**
36
+ * Perform a sequential inclusive postfix reverse scan over the statically-sized \p input array, seeded with the specified \p postfix. The aggregate is returned.
37
+ */
38
+ template <
39
+ int LENGTH,
40
+ typename T,
41
+ typename ScanOp>
42
+ __device__ __forceinline__ T ThreadReverseScanInclusive(
43
+ const T (&input)[LENGTH],
44
+ T (&output)[LENGTH],
45
+ ScanOp scan_op,
46
+ const T postfix)
47
+ {
48
+ T inclusive = postfix;
49
+ #pragma unroll
50
+ for (int i = LENGTH - 1; i >= 0; --i) {
51
+ inclusive = scan_op(inclusive, input[i]);
52
+ output[i] = inclusive;
53
+ }
54
+ return inclusive;
55
+ }
56
+
57
+ /**
58
+ * Perform a sequential exclusive postfix reverse scan over the statically-sized \p input array, seeded with the specified \p postfix. The aggregate is returned.
59
+ */
60
+ template <
61
+ int LENGTH,
62
+ typename T,
63
+ typename ScanOp>
64
+ __device__ __forceinline__ T ThreadReverseScanExclusive(
65
+ const T (&input)[LENGTH],
66
+ T (&output)[LENGTH],
67
+ ScanOp scan_op,
68
+ const T postfix)
69
+ {
70
+ // Careful, output maybe be aliased to input
71
+ T exclusive = postfix;
72
+ T inclusive;
73
+ #pragma unroll
74
+ for (int i = LENGTH - 1; i >= 0; --i) {
75
+ inclusive = scan_op(exclusive, input[i]);
76
+ output[i] = exclusive;
77
+ exclusive = inclusive;
78
+ }
79
+ return inclusive;
80
+ }
81
+
82
+
83
+ /**
84
+ * \brief WarpReverseScan provides SHFL-based variants of parallel postfix scan of items partitioned across a CUDA thread warp.
85
+ *
86
+ * LOGICAL_WARP_THREADS must be a power-of-two
87
+ */
88
+ template <
89
+ typename T, ///< Data type being scanned
90
+ int LOGICAL_WARP_THREADS ///< Number of threads per logical warp
91
+ >
92
+ struct WarpReverseScan {
93
+ //---------------------------------------------------------------------
94
+ // Constants and type definitions
95
+ //---------------------------------------------------------------------
96
+
97
+ /// Whether the logical warp size and the PTX warp size coincide
98
+
99
+ // In hipcub, warp_threads is defined as HIPCUB_WARP_THREADS ::rocprim::warp_size()
100
+ // While in cub, it's defined as a macro that takes a redundant unused argument.
101
+ #ifndef USE_ROCM
102
+ #define WARP_THREADS CUB_WARP_THREADS(0)
103
+ #else
104
+ // ROCm 7.0+: HIPCUB_WARP_THREADS (rocprim::warp_size()) is no longer constexpr.
105
+ // We need a compile-time constant for IS_ARCH_WARP below.
106
+ // See: https://rocm.docs.amd.com/en/latest/about/release-notes.html
107
+ #if defined(__AMDGCN_WAVEFRONT_SIZE)
108
+ // Deprecated but still available and constexpr in ROCm 7.x
109
+ #define WARP_THREADS __AMDGCN_WAVEFRONT_SIZE
110
+ #elif defined(__gfx942__) || defined(__gfx941__) || defined(__gfx940__)
111
+ // AMD Instinct MI300 series (CDNA3) - 64-wide wavefronts
112
+ #define WARP_THREADS 64
113
+ #elif defined(__gfx90a__)
114
+ // AMD Instinct MI200 series (CDNA2) - 64-wide wavefronts
115
+ #define WARP_THREADS 64
116
+ #elif defined(__gfx908__)
117
+ // AMD Instinct MI100 (CDNA1) - 64-wide wavefronts
118
+ #define WARP_THREADS 64
119
+ #elif defined(__gfx906__) || defined(__gfx900__)
120
+ // AMD Instinct MI50/MI60 (Vega) - 64-wide wavefronts
121
+ #define WARP_THREADS 64
122
+ #elif defined(__gfx1100__) || defined(__gfx1101__) || defined(__gfx1102__) || defined(__gfx1103__)
123
+ // AMD Radeon RX 7000 series (RDNA3) - 32-wide wavefronts
124
+ #define WARP_THREADS 32
125
+ #elif defined(__gfx1030__) || defined(__gfx1031__) || defined(__gfx1032__) || defined(__gfx1034__)
126
+ // AMD Radeon RX 6000 series (RDNA2) - 32-wide wavefronts
127
+ #define WARP_THREADS 32
128
+ #elif defined(__gfx1010__) || defined(__gfx1011__) || defined(__gfx1012__)
129
+ // AMD Radeon RX 5000 series (RDNA1) - 32-wide wavefronts
130
+ #define WARP_THREADS 32
131
+ #else
132
+ // Unknown architecture - default to 64 (CDNA/GCN)
133
+ // This may not be optimal for RDNA GPUs
134
+ #pragma message("Warning: Unknown AMD GPU architecture. Defaulting WARP_THREADS to 64. " \
135
+ "For RDNA GPUs (gfx10xx/gfx11xx), this should be 32.")
136
+ #define WARP_THREADS 64
137
+ #endif
138
+ #endif
139
+ static constexpr bool IS_ARCH_WARP = (LOGICAL_WARP_THREADS == WARP_THREADS);
140
+ /// The number of warp scan steps
141
+ static constexpr int STEPS = cub::Log2<LOGICAL_WARP_THREADS>::VALUE;
142
+ static_assert(LOGICAL_WARP_THREADS == 1 << STEPS);
143
+
144
+
145
+ //---------------------------------------------------------------------
146
+ // Thread fields
147
+ //---------------------------------------------------------------------
148
+
149
+ /// Lane index in logical warp
150
+ unsigned int lane_id;
151
+
152
+ /// Logical warp index in 32-thread physical warp
153
+ unsigned int warp_id;
154
+
155
+ /// 32-thread physical warp member mask of logical warp
156
+ unsigned int member_mask;
157
+
158
+ //---------------------------------------------------------------------
159
+ // Construction
160
+ //---------------------------------------------------------------------
161
+
162
+ /// Constructor
163
+ explicit __device__ __forceinline__
164
+ WarpReverseScan()
165
+ #ifndef USE_ROCM
166
+ : lane_id(threadIdx.x & 0x1f) // CUDA: 32-thread warps, mask = 31
167
+ #else
168
+ : lane_id(threadIdx.x & (WARP_THREADS - 1)) // ROCm: use actual wavefront size (64 or 32)
169
+ #endif
170
+ , warp_id(IS_ARCH_WARP ? 0 : (lane_id / LOGICAL_WARP_THREADS))
171
+ , member_mask(cub::WarpMask<LOGICAL_WARP_THREADS>(warp_id))
172
+ {
173
+ if (!IS_ARCH_WARP) {
174
+ lane_id = lane_id % LOGICAL_WARP_THREADS;
175
+ }
176
+ }
177
+
178
+
179
+ /// Broadcast
180
+ __device__ __forceinline__ T Broadcast(
181
+ T input, ///< [in] The value to broadcast
182
+ int src_lane) ///< [in] Which warp lane is to do the broadcasting
183
+ {
184
+ return cub::ShuffleIndex<LOGICAL_WARP_THREADS>(input, src_lane, member_mask);
185
+ }
186
+
187
+
188
+ /// Inclusive scan
189
+ template <typename ScanOpT>
190
+ __device__ __forceinline__ void InclusiveReverseScan(
191
+ T input, ///< [in] Calling thread's input item.
192
+ T &inclusive_output, ///< [out] Calling thread's output item. May be aliased with \p input.
193
+ ScanOpT scan_op) ///< [in] Binary scan operator
194
+ {
195
+ inclusive_output = input;
196
+ #pragma unroll
197
+ for (int STEP = 0; STEP < STEPS; STEP++) {
198
+ int offset = 1 << STEP;
199
+ T temp = cub::ShuffleDown<LOGICAL_WARP_THREADS>(
200
+ inclusive_output, offset, LOGICAL_WARP_THREADS - 1, member_mask
201
+ );
202
+ // Perform scan op if from a valid peer
203
+ inclusive_output = static_cast<int>(lane_id) >= LOGICAL_WARP_THREADS - offset
204
+ ? inclusive_output : scan_op(temp, inclusive_output);
205
+ }
206
+ }
207
+
208
+ /// Exclusive scan
209
+ // Get exclusive from inclusive
210
+ template <typename ScanOpT>
211
+ __device__ __forceinline__ void ExclusiveReverseScan(
212
+ T input, ///< [in] Calling thread's input item.
213
+ T &exclusive_output, ///< [out] Calling thread's output item. May be aliased with \p input.
214
+ ScanOpT scan_op, ///< [in] Binary scan operator
215
+ T &warp_aggregate) ///< [out] Warp-wide aggregate reduction of input items.
216
+ {
217
+ T inclusive_output;
218
+ InclusiveReverseScan(input, inclusive_output, scan_op);
219
+ warp_aggregate = cub::ShuffleIndex<LOGICAL_WARP_THREADS>(inclusive_output, 0, member_mask);
220
+ // initial value unknown
221
+ exclusive_output = cub::ShuffleDown<LOGICAL_WARP_THREADS>(
222
+ inclusive_output, 1, LOGICAL_WARP_THREADS - 1, member_mask
223
+ );
224
+ }
225
+
226
+ /**
227
+ * \brief Computes both inclusive and exclusive reverse scans using the specified binary scan functor across the calling warp. Because no initial value is supplied, the \p exclusive_output computed for the last <em>warp-lane</em> is undefined.
228
+ */
229
+ template <typename ScanOpT>
230
+ __device__ __forceinline__ void ReverseScan(
231
+ T input, ///< [in] Calling thread's input item.
232
+ T &inclusive_output, ///< [out] Calling thread's inclusive-scan output item.
233
+ T &exclusive_output, ///< [out] Calling thread's exclusive-scan output item.
234
+ ScanOpT scan_op) ///< [in] Binary scan operator
235
+ {
236
+ InclusiveReverseScan(input, inclusive_output, scan_op);
237
+ // initial value unknown
238
+ exclusive_output = cub::ShuffleDown<LOGICAL_WARP_THREADS>(
239
+ inclusive_output, 1, LOGICAL_WARP_THREADS - 1, member_mask
240
+ );
241
+ }
242
+
243
+ };
244
+
245
+ /**
246
+ * \brief BlockReverseScan provides variants of raking-based parallel postfix scan across a CUDA thread block.
247
+ */
248
+ template <
249
+ typename T, ///< Data type being scanned
250
+ int BLOCK_DIM_X, ///< The thread block length in threads along the X dimension
251
+ bool MEMOIZE=false ///< Whether or not to buffer outer raking scan partials to incur fewer shared memory reads at the expense of higher register pressure
252
+ >
253
+ struct BlockReverseScan {
254
+ //---------------------------------------------------------------------
255
+ // Types and constants
256
+ //---------------------------------------------------------------------
257
+
258
+ /// Constants
259
+ /// The thread block size in threads
260
+ static constexpr int BLOCK_THREADS = BLOCK_DIM_X;
261
+
262
+ /// Layout type for padded thread block raking grid
263
+ using BlockRakingLayout = cub::BlockRakingLayout<T, BLOCK_THREADS>;
264
+ // The number of reduction elements is not a multiple of the number of raking threads for now
265
+ static_assert(BlockRakingLayout::UNGUARDED);
266
+
267
+ /// Number of raking threads
268
+ static constexpr int RAKING_THREADS = BlockRakingLayout::RAKING_THREADS;
269
+ /// Number of raking elements per warp synchronous raking thread
270
+ static constexpr int SEGMENT_LENGTH = BlockRakingLayout::SEGMENT_LENGTH;
271
+ /// Cooperative work can be entirely warp synchronous
272
+ static constexpr bool WARP_SYNCHRONOUS = (int(BLOCK_THREADS) == int(RAKING_THREADS));
273
+
274
+ /// WarpReverseScan utility type
275
+ using WarpReverseScan = WarpReverseScan<T, RAKING_THREADS>;
276
+
277
+ /// Shared memory storage layout type
278
+ struct _TempStorage {
279
+ typename BlockRakingLayout::TempStorage raking_grid; ///< Padded thread block raking grid
280
+ };
281
+
282
+
283
+ /// Alias wrapper allowing storage to be unioned
284
+ struct TempStorage : cub::Uninitialized<_TempStorage> {};
285
+
286
+
287
+ //---------------------------------------------------------------------
288
+ // Per-thread fields
289
+ //---------------------------------------------------------------------
290
+
291
+ // Thread fields
292
+ _TempStorage &temp_storage;
293
+ unsigned int linear_tid;
294
+ T cached_segment[SEGMENT_LENGTH];
295
+
296
+
297
+ //---------------------------------------------------------------------
298
+ // Utility methods
299
+ //---------------------------------------------------------------------
300
+
301
+ /// Performs upsweep raking reduction, returning the aggregate
302
+ template <typename ScanOp>
303
+ __device__ __forceinline__ T Upsweep(ScanOp scan_op) {
304
+ T *smem_raking_ptr = BlockRakingLayout::RakingPtr(temp_storage.raking_grid, linear_tid);
305
+ // Read data into registers
306
+ #pragma unroll
307
+ for (int i = 0; i < SEGMENT_LENGTH; ++i) { cached_segment[i] = smem_raking_ptr[i]; }
308
+ T raking_partial = cached_segment[SEGMENT_LENGTH - 1];
309
+ #pragma unroll
310
+ for (int i = SEGMENT_LENGTH - 2; i >= 0; --i) {
311
+ raking_partial = scan_op(raking_partial, cached_segment[i]);
312
+ }
313
+ return raking_partial;
314
+ }
315
+
316
+
317
+ /// Performs exclusive downsweep raking scan
318
+ template <typename ScanOp>
319
+ __device__ __forceinline__ void ExclusiveDownsweep(
320
+ ScanOp scan_op,
321
+ T raking_partial)
322
+ {
323
+ T *smem_raking_ptr = BlockRakingLayout::RakingPtr(temp_storage.raking_grid, linear_tid);
324
+ // Read data back into registers
325
+ if (!MEMOIZE) {
326
+ #pragma unroll
327
+ for (int i = 0; i < SEGMENT_LENGTH; ++i) { cached_segment[i] = smem_raking_ptr[i]; }
328
+ }
329
+ ThreadReverseScanExclusive(cached_segment, cached_segment, scan_op, raking_partial);
330
+ // Write data back to smem
331
+ #pragma unroll
332
+ for (int i = 0; i < SEGMENT_LENGTH; ++i) { smem_raking_ptr[i] = cached_segment[i]; }
333
+ }
334
+
335
+
336
+ //---------------------------------------------------------------------
337
+ // Constructors
338
+ //---------------------------------------------------------------------
339
+
340
+ /// Constructor
341
+ __device__ __forceinline__ BlockReverseScan(
342
+ TempStorage &temp_storage)
343
+ :
344
+ temp_storage(temp_storage.Alias()),
345
+ linear_tid(cub::RowMajorTid(BLOCK_DIM_X, 1, 1))
346
+ {}
347
+
348
+
349
+ /// Computes an exclusive thread block-wide postfix scan using the specified binary \p scan_op functor. Each thread contributes one input element. the call-back functor \p block_postfix_callback_op is invoked by the first warp in the block, and the value returned by <em>lane</em><sub>0</sub> in that warp is used as the "seed" value that logically postfixes the thread block's scan inputs. Also provides every thread with the block-wide \p block_aggregate of all inputs.
350
+ template <
351
+ typename ScanOp,
352
+ typename BlockPostfixCallbackOp>
353
+ __device__ __forceinline__ void ExclusiveReverseScan(
354
+ T input, ///< [in] Calling thread's input item
355
+ T &exclusive_output, ///< [out] Calling thread's output item (may be aliased to \p input)
356
+ ScanOp scan_op, ///< [in] Binary scan operator
357
+ BlockPostfixCallbackOp &block_postfix_callback_op) ///< [in-out] <b>[<em>warp</em><sub>0</sub> only]</b> Call-back functor for specifying a thread block-wide postfix to be applied to all inputs.
358
+ {
359
+ if (WARP_SYNCHRONOUS) {
360
+ // Short-circuit directly to warp-synchronous scan
361
+ T block_aggregate;
362
+ WarpReverseScan warp_scan;
363
+ warp_scan.ExclusiveReverseScan(input, exclusive_output, scan_op, block_aggregate);
364
+ // Obtain warp-wide postfix in lane0, then broadcast to other lanes
365
+ T block_postfix = block_postfix_callback_op(block_aggregate);
366
+ block_postfix = warp_scan.Broadcast(block_postfix, 0);
367
+ exclusive_output = linear_tid == BLOCK_THREADS - 1 ? block_postfix : scan_op(block_postfix, exclusive_output);
368
+ } else {
369
+ // Place thread partial into shared memory raking grid
370
+ T *placement_ptr = BlockRakingLayout::PlacementPtr(temp_storage.raking_grid, linear_tid);
371
+ detail::uninitialized_copy(placement_ptr, input);
372
+ __syncthreads();
373
+ // Reduce parallelism down to just raking threads
374
+ if (linear_tid < RAKING_THREADS) {
375
+ WarpReverseScan warp_scan;
376
+ // Raking upsweep reduction across shared partials
377
+ T upsweep_partial = Upsweep(scan_op);
378
+ // Warp-synchronous scan
379
+ T exclusive_partial, block_aggregate;
380
+ warp_scan.ExclusiveReverseScan(upsweep_partial, exclusive_partial, scan_op, block_aggregate);
381
+ // Obtain block-wide postfix in lane0, then broadcast to other lanes
382
+ T block_postfix = block_postfix_callback_op(block_aggregate);
383
+ block_postfix = warp_scan.Broadcast(block_postfix, 0);
384
+ // Update postfix with warpscan exclusive partial
385
+ T downsweep_postfix = linear_tid == RAKING_THREADS - 1
386
+ ? block_postfix : scan_op(block_postfix, exclusive_partial);
387
+ // Exclusive raking downsweep scan
388
+ ExclusiveDownsweep(scan_op, downsweep_postfix);
389
+ }
390
+ __syncthreads();
391
+ // Grab thread postfix from shared memory
392
+ exclusive_output = *placement_ptr;
393
+
394
+ // // Compute warp scan in each warp.
395
+ // // The exclusive output from the last lane in each warp is invalid.
396
+ // T inclusive_output;
397
+ // WarpReverseScan warp_scan;
398
+ // warp_scan.ReverseScan(input, inclusive_output, exclusive_output, scan_op);
399
+
400
+ // // Compute the warp-wide postfix and block-wide aggregate for each warp. Warp postfix for the last warp is invalid.
401
+ // T block_aggregate;
402
+ // T warp_postfix = ComputeWarpPostfix(scan_op, inclusive_output, block_aggregate);
403
+
404
+ // // Apply warp postfix to our lane's partial
405
+ // if (warp_id != 0) {
406
+ // exclusive_output = scan_op(warp_postfix, exclusive_output);
407
+ // if (lane_id == 0) { exclusive_output = warp_postfix; }
408
+ // }
409
+
410
+ // // Use the first warp to determine the thread block postfix, returning the result in lane0
411
+ // if (warp_id == 0) {
412
+ // T block_postfix = block_postfix_callback_op(block_aggregate);
413
+ // if (lane_id == 0) {
414
+ // // Share the postfix with all threads
415
+ // detail::uninitialized_copy(&temp_storage.block_postfix,
416
+ // block_postfix);
417
+
418
+ // exclusive_output = block_postfix; // The block postfix is the exclusive output for tid0
419
+ // }
420
+ // }
421
+
422
+ // __syncthreads();
423
+
424
+ // // Incorporate thread block postfix into outputs
425
+ // T block_postfix = temp_storage.block_postfix;
426
+ // if (linear_tid > 0) { exclusive_output = scan_op(block_postfix, exclusive_output); }
427
+ }
428
+ }
429
+
430
+
431
+ /**
432
+ * \brief Computes an inclusive block-wide postfix scan using the specified binary \p scan_op functor. Each thread contributes an array of consecutive input elements. the call-back functor \p block_postfix_callback_op is invoked by the first warp in the block, and the value returned by <em>lane</em><sub>0</sub> in that warp is used as the "seed" value that logically postfixes the thread block's scan inputs. Also provides every thread with the block-wide \p block_aggregate of all inputs.
433
+ */
434
+ template <
435
+ int ITEMS_PER_THREAD,
436
+ typename ScanOp,
437
+ typename BlockPostfixCallbackOp>
438
+ __device__ __forceinline__ void InclusiveReverseScan(
439
+ T (&input)[ITEMS_PER_THREAD], ///< [in] Calling thread's input items
440
+ T (&output)[ITEMS_PER_THREAD], ///< [out] Calling thread's output items (may be aliased to \p input)
441
+ ScanOp scan_op, ///< [in] Binary scan functor
442
+ BlockPostfixCallbackOp &block_postfix_callback_op) ///< [in-out] <b>[<em>warp</em><sub>0</sub> only]</b> Call-back functor for specifying a block-wide postfix to be applied to the logical input sequence.
443
+ {
444
+ // Reduce consecutive thread items in registers
445
+ T thread_postfix = ThreadReverseReduce(input, scan_op);
446
+ // Exclusive thread block-scan
447
+ ExclusiveReverseScan(thread_postfix, thread_postfix, scan_op, block_postfix_callback_op);
448
+ // Inclusive scan in registers with postfix as seed
449
+ ThreadReverseScanInclusive(input, output, scan_op, thread_postfix);
450
+ }
451
+
452
+ };
vendor/mamba-og/csrc/selective_scan/selective_scan.cpp ADDED
@@ -0,0 +1,497 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ #include <c10/cuda/CUDAGuard.h>
6
+ #include <c10/cuda/CUDAStream.h>
7
+ #include <torch/python.h>
8
+ #include <vector>
9
+
10
+ #include "selective_scan.h"
11
+
12
+ #define CHECK_SHAPE(x, ...) TORCH_CHECK(x.sizes() == torch::IntArrayRef({__VA_ARGS__}), #x " must have shape (" #__VA_ARGS__ ")")
13
+
14
+ #define DISPATCH_ITYPE_FLOAT_AND_HALF_AND_BF16(ITYPE, NAME, ...) \
15
+ if (ITYPE == at::ScalarType::Half) { \
16
+ using input_t = at::Half; \
17
+ __VA_ARGS__(); \
18
+ } else if (ITYPE == at::ScalarType::BFloat16) { \
19
+ using input_t = at::BFloat16; \
20
+ __VA_ARGS__(); \
21
+ } else if (ITYPE == at::ScalarType::Float) { \
22
+ using input_t = float; \
23
+ __VA_ARGS__(); \
24
+ } else { \
25
+ AT_ERROR(#NAME, " not implemented for input type '", toString(ITYPE), "'"); \
26
+ }
27
+
28
+ #define DISPATCH_WTYPE_FLOAT_AND_HALF_AND_BF16(WTYPE, NAME, ...) \
29
+ if (WTYPE == at::ScalarType::Half) { \
30
+ using weight_t = at::Half; \
31
+ __VA_ARGS__(); \
32
+ } else if (WTYPE == at::ScalarType::BFloat16) { \
33
+ using weight_t = at::BFloat16; \
34
+ __VA_ARGS__(); \
35
+ } else if (WTYPE == at::ScalarType::Float) { \
36
+ using weight_t = float; \
37
+ __VA_ARGS__(); \
38
+ } else { \
39
+ AT_ERROR(#NAME, " not implemented for weight type '", toString(WTYPE), "'"); \
40
+ }
41
+
42
+ #define DISPATCH_WTYPE_FLOAT_AND_COMPLEX(WTYPE, NAME, ...) \
43
+ if (WTYPE == at::ScalarType::Float) { \
44
+ using weight_t = float; \
45
+ __VA_ARGS__(); \
46
+ } else if (WTYPE == at::ScalarType::ComplexFloat) { \
47
+ using weight_t = c10::complex<float>; \
48
+ __VA_ARGS__(); \
49
+ } else { \
50
+ AT_ERROR(#NAME, " not implemented for weight type '", toString(WTYPE), "'"); \
51
+ }
52
+
53
+ template<typename input_t, typename weight_t>
54
+ void selective_scan_fwd_cuda(SSMParamsBase &params, cudaStream_t stream);
55
+
56
+ template <typename input_t, typename weight_t>
57
+ void selective_scan_bwd_cuda(SSMParamsBwd &params, cudaStream_t stream);
58
+
59
+ void set_ssm_params_fwd(SSMParamsBase &params,
60
+ // sizes
61
+ const size_t batch,
62
+ const size_t dim,
63
+ const size_t seqlen,
64
+ const size_t dstate,
65
+ const size_t n_groups,
66
+ const size_t n_chunks,
67
+ const bool is_variable_B,
68
+ const bool is_variable_C,
69
+ // device pointers
70
+ const at::Tensor u,
71
+ const at::Tensor delta,
72
+ const at::Tensor A,
73
+ const at::Tensor B,
74
+ const at::Tensor C,
75
+ const at::Tensor out,
76
+ const at::Tensor z,
77
+ const at::Tensor out_z,
78
+ void* D_ptr,
79
+ void* delta_bias_ptr,
80
+ void* x_ptr,
81
+ bool has_z,
82
+ bool delta_softplus) {
83
+
84
+ // Reset the parameters
85
+ memset(&params, 0, sizeof(params));
86
+
87
+ params.batch = batch;
88
+ params.dim = dim;
89
+ params.seqlen = seqlen;
90
+ params.dstate = dstate;
91
+ params.n_groups = n_groups;
92
+ params.n_chunks = n_chunks;
93
+ params.dim_ngroups_ratio = dim / n_groups;
94
+
95
+ params.delta_softplus = delta_softplus;
96
+
97
+ params.is_variable_B = is_variable_B;
98
+ params.is_variable_C = is_variable_C;
99
+
100
+ // Set the pointers and strides.
101
+ params.u_ptr = u.data_ptr();
102
+ params.delta_ptr = delta.data_ptr();
103
+ params.A_ptr = A.data_ptr();
104
+ params.B_ptr = B.data_ptr();
105
+ params.C_ptr = C.data_ptr();
106
+ params.D_ptr = D_ptr;
107
+ params.delta_bias_ptr = delta_bias_ptr;
108
+ params.out_ptr = out.data_ptr();
109
+ params.x_ptr = x_ptr;
110
+ params.z_ptr = has_z ? z.data_ptr() : nullptr;
111
+ params.out_z_ptr = has_z ? out_z.data_ptr() : nullptr;
112
+ // All stride are in elements, not bytes.
113
+ params.A_d_stride = A.stride(0);
114
+ params.A_dstate_stride = A.stride(1);
115
+ if (!is_variable_B) {
116
+ params.B_d_stride = B.stride(0);
117
+ } else {
118
+ params.B_batch_stride = B.stride(0);
119
+ params.B_group_stride = B.stride(1);
120
+ }
121
+ params.B_dstate_stride = !is_variable_B ? B.stride(1) : B.stride(2);
122
+ if (!is_variable_C) {
123
+ params.C_d_stride = C.stride(0);
124
+ } else {
125
+ params.C_batch_stride = C.stride(0);
126
+ params.C_group_stride = C.stride(1);
127
+ }
128
+ params.C_dstate_stride = !is_variable_C ? C.stride(1) : C.stride(2);
129
+ params.u_batch_stride = u.stride(0);
130
+ params.u_d_stride = u.stride(1);
131
+ params.delta_batch_stride = delta.stride(0);
132
+ params.delta_d_stride = delta.stride(1);
133
+ if (has_z) {
134
+ params.z_batch_stride = z.stride(0);
135
+ params.z_d_stride = z.stride(1);
136
+ params.out_z_batch_stride = out_z.stride(0);
137
+ params.out_z_d_stride = out_z.stride(1);
138
+ }
139
+ params.out_batch_stride = out.stride(0);
140
+ params.out_d_stride = out.stride(1);
141
+ }
142
+
143
+ void set_ssm_params_bwd(SSMParamsBwd &params,
144
+ // sizes
145
+ const size_t batch,
146
+ const size_t dim,
147
+ const size_t seqlen,
148
+ const size_t dstate,
149
+ const size_t n_groups,
150
+ const size_t n_chunks,
151
+ const bool is_variable_B,
152
+ const bool is_variable_C,
153
+ // device pointers
154
+ const at::Tensor u,
155
+ const at::Tensor delta,
156
+ const at::Tensor A,
157
+ const at::Tensor B,
158
+ const at::Tensor C,
159
+ const at::Tensor z,
160
+ const at::Tensor out,
161
+ const at::Tensor out_z,
162
+ void* D_ptr,
163
+ void* delta_bias_ptr,
164
+ void* x_ptr,
165
+ const at::Tensor dout,
166
+ const at::Tensor du,
167
+ const at::Tensor ddelta,
168
+ const at::Tensor dA,
169
+ const at::Tensor dB,
170
+ const at::Tensor dC,
171
+ const at::Tensor dz,
172
+ void* dD_ptr,
173
+ void* ddelta_bias_ptr,
174
+ bool has_z,
175
+ bool delta_softplus,
176
+ bool recompute_out_z) {
177
+ // Pass in "dout" instead of "out", we're not gonna use "out" unless we have z
178
+ set_ssm_params_fwd(params, batch, dim, seqlen, dstate, n_groups, n_chunks, is_variable_B, is_variable_C,
179
+ u, delta, A, B, C, has_z ? out : dout,
180
+ has_z ? z : dout,
181
+ // If not recompute_out_z, pass dout instead of out_z.
182
+ // This won't be used by the bwd kernel
183
+ recompute_out_z ? out_z : dout,
184
+ D_ptr, delta_bias_ptr, x_ptr, has_z, delta_softplus);
185
+ if (!recompute_out_z) { params.out_z_ptr = nullptr; }
186
+
187
+ // Set the pointers and strides.
188
+ params.dout_ptr = dout.data_ptr();
189
+ params.du_ptr = du.data_ptr();
190
+ params.dA_ptr = dA.data_ptr();
191
+ params.dB_ptr = dB.data_ptr();
192
+ params.dC_ptr = dC.data_ptr();
193
+ params.dD_ptr = dD_ptr;
194
+ params.ddelta_ptr = ddelta.data_ptr();
195
+ params.ddelta_bias_ptr = ddelta_bias_ptr;
196
+ params.dz_ptr = has_z ? dz.data_ptr() : nullptr;
197
+ // All stride are in elements, not bytes.
198
+ params.dout_batch_stride = dout.stride(0);
199
+ params.dout_d_stride = dout.stride(1);
200
+ params.dA_d_stride = dA.stride(0);
201
+ params.dA_dstate_stride = dA.stride(1);
202
+ if (!is_variable_B) {
203
+ params.dB_d_stride = dB.stride(0);
204
+ } else {
205
+ params.dB_batch_stride = dB.stride(0);
206
+ params.dB_group_stride = dB.stride(1);
207
+ }
208
+ params.dB_dstate_stride = !is_variable_B ? dB.stride(1) : dB.stride(2);
209
+ if (!is_variable_C) {
210
+ params.dC_d_stride = dC.stride(0);
211
+ } else {
212
+ params.dC_batch_stride = dC.stride(0);
213
+ params.dC_group_stride = dC.stride(1);
214
+ }
215
+ params.dC_dstate_stride = !is_variable_C ? dC.stride(1) : dC.stride(2);
216
+ params.du_batch_stride = du.stride(0);
217
+ params.du_d_stride = du.stride(1);
218
+ params.ddelta_batch_stride = ddelta.stride(0);
219
+ params.ddelta_d_stride = ddelta.stride(1);
220
+ if (has_z) {
221
+ params.dz_batch_stride = dz.stride(0);
222
+ params.dz_d_stride = dz.stride(1);
223
+ }
224
+ }
225
+
226
+ std::vector<at::Tensor>
227
+ selective_scan_fwd(const at::Tensor &u, const at::Tensor &delta,
228
+ const at::Tensor &A, const at::Tensor &B, const at::Tensor &C,
229
+ const c10::optional<at::Tensor> &D_,
230
+ const c10::optional<at::Tensor> &z_,
231
+ const c10::optional<at::Tensor> &delta_bias_,
232
+ bool delta_softplus) {
233
+ auto input_type = u.scalar_type();
234
+ auto weight_type = A.scalar_type();
235
+ TORCH_CHECK(input_type == at::ScalarType::Float || input_type == at::ScalarType::Half || input_type == at::ScalarType::BFloat16);
236
+ TORCH_CHECK(weight_type == at::ScalarType::Float || weight_type == at::ScalarType::ComplexFloat);
237
+
238
+ const bool is_variable_B = B.dim() >= 3;
239
+ const bool is_variable_C = C.dim() >= 3;
240
+ const bool is_complex = weight_type == at::ScalarType::ComplexFloat;
241
+
242
+ TORCH_CHECK(delta.scalar_type() == input_type);
243
+ TORCH_CHECK(B.scalar_type() == (!is_variable_B ? weight_type : input_type));
244
+ TORCH_CHECK(C.scalar_type() == (!is_variable_C ? weight_type : input_type));
245
+
246
+ TORCH_CHECK(u.is_cuda());
247
+ TORCH_CHECK(delta.is_cuda());
248
+ TORCH_CHECK(A.is_cuda());
249
+ TORCH_CHECK(B.is_cuda());
250
+ TORCH_CHECK(C.is_cuda());
251
+
252
+ TORCH_CHECK(u.stride(-1) == 1 || u.size(-1) == 1);
253
+ TORCH_CHECK(delta.stride(-1) == 1 || delta.size(-1) == 1);
254
+
255
+ const auto sizes = u.sizes();
256
+ const int batch_size = sizes[0];
257
+ const int dim = sizes[1];
258
+ const int seqlen = sizes[2];
259
+ const int dstate = A.size(1);
260
+ const int n_groups = is_variable_B ? B.size(1) : 1;
261
+
262
+ TORCH_CHECK(dstate <= 256, "selective_scan only supports state dimension <= 256");
263
+
264
+ CHECK_SHAPE(u, batch_size, dim, seqlen);
265
+ CHECK_SHAPE(delta, batch_size, dim, seqlen);
266
+ CHECK_SHAPE(A, dim, dstate);
267
+ if (!is_variable_B) {
268
+ CHECK_SHAPE(B, dim, dstate);
269
+ } else {
270
+ CHECK_SHAPE(B, batch_size, n_groups, dstate, !is_complex ? seqlen : seqlen * 2);
271
+ TORCH_CHECK(B.stride(-1) == 1 || B.size(-1) == 1);
272
+ }
273
+ if (!is_variable_C) {
274
+ CHECK_SHAPE(C, dim, dstate);
275
+ } else {
276
+ CHECK_SHAPE(C, batch_size, n_groups, dstate, !is_complex ? seqlen: seqlen * 2);
277
+ TORCH_CHECK(C.stride(-1) == 1 || C.size(-1) == 1);
278
+ }
279
+
280
+ if (D_.has_value()) {
281
+ auto D = D_.value();
282
+ TORCH_CHECK(D.scalar_type() == at::ScalarType::Float);
283
+ TORCH_CHECK(D.is_cuda());
284
+ TORCH_CHECK(D.stride(-1) == 1 || D.size(-1) == 1);
285
+ CHECK_SHAPE(D, dim);
286
+ }
287
+
288
+ if (delta_bias_.has_value()) {
289
+ auto delta_bias = delta_bias_.value();
290
+ TORCH_CHECK(delta_bias.scalar_type() == at::ScalarType::Float);
291
+ TORCH_CHECK(delta_bias.is_cuda());
292
+ TORCH_CHECK(delta_bias.stride(-1) == 1 || delta_bias.size(-1) == 1);
293
+ CHECK_SHAPE(delta_bias, dim);
294
+ }
295
+
296
+ at::Tensor z, out_z;
297
+ const bool has_z = z_.has_value();
298
+ if (has_z) {
299
+ z = z_.value();
300
+ TORCH_CHECK(z.scalar_type() == input_type);
301
+ TORCH_CHECK(z.is_cuda());
302
+ TORCH_CHECK(z.stride(-1) == 1 || z.size(-1) == 1);
303
+ CHECK_SHAPE(z, batch_size, dim, seqlen);
304
+ out_z = torch::empty_like(z);
305
+ }
306
+
307
+ const int n_chunks = (seqlen + 2048 - 1) / 2048;
308
+ // const int n_chunks = (seqlen + 1024 - 1) / 1024;
309
+ // at::Tensor out = torch::empty_like(u);
310
+ // Right now u has BHL layout and delta has HBL layout, and we want out to have HBL layout
311
+ at::Tensor out = torch::empty_like(delta);
312
+ at::Tensor x;
313
+ x = torch::empty({batch_size, dim, n_chunks, dstate * 2}, u.options().dtype(weight_type));
314
+
315
+ SSMParamsBase params;
316
+ set_ssm_params_fwd(params, batch_size, dim, seqlen, dstate, n_groups, n_chunks, is_variable_B, is_variable_C,
317
+ u, delta, A, B, C, out, z, out_z,
318
+ D_.has_value() ? D_.value().data_ptr() : nullptr,
319
+ delta_bias_.has_value() ? delta_bias_.value().data_ptr() : nullptr,
320
+ x.data_ptr(),
321
+ has_z,
322
+ delta_softplus);
323
+
324
+ // Otherwise the kernel will be launched from cuda:0 device
325
+ // Cast to char to avoid compiler warning about narrowing
326
+ at::cuda::CUDAGuard device_guard{u.device()};
327
+ auto stream = at::cuda::getCurrentCUDAStream().stream();
328
+ DISPATCH_ITYPE_FLOAT_AND_HALF_AND_BF16(u.scalar_type(), "selective_scan_fwd", [&] {
329
+ DISPATCH_WTYPE_FLOAT_AND_COMPLEX(A.scalar_type(), "selective_scan_fwd", [&] {
330
+ selective_scan_fwd_cuda<input_t, weight_t>(params, stream);
331
+ });
332
+ });
333
+ std::vector<at::Tensor> result = {out, x};
334
+ if (has_z) { result.push_back(out_z); }
335
+ return result;
336
+ }
337
+
338
+ std::vector<at::Tensor>
339
+ selective_scan_bwd(const at::Tensor &u, const at::Tensor &delta,
340
+ const at::Tensor &A, const at::Tensor &B, const at::Tensor &C,
341
+ const c10::optional<at::Tensor> &D_,
342
+ const c10::optional<at::Tensor> &z_,
343
+ const c10::optional<at::Tensor> &delta_bias_,
344
+ const at::Tensor &dout,
345
+ const c10::optional<at::Tensor> &x_,
346
+ const c10::optional<at::Tensor> &out_,
347
+ c10::optional<at::Tensor> &dz_,
348
+ bool delta_softplus,
349
+ bool recompute_out_z) {
350
+ auto input_type = u.scalar_type();
351
+ auto weight_type = A.scalar_type();
352
+ TORCH_CHECK(input_type == at::ScalarType::Float || input_type == at::ScalarType::Half || input_type == at::ScalarType::BFloat16);
353
+ TORCH_CHECK(weight_type == at::ScalarType::Float || weight_type == at::ScalarType::ComplexFloat);
354
+
355
+ const bool is_variable_B = B.dim() >= 3;
356
+ const bool is_variable_C = C.dim() >= 3;
357
+ const bool is_complex = weight_type == at::ScalarType::ComplexFloat;
358
+
359
+ TORCH_CHECK(delta.scalar_type() == input_type);
360
+ TORCH_CHECK(B.scalar_type() == (!is_variable_B ? weight_type : input_type));
361
+ TORCH_CHECK(C.scalar_type() == (!is_variable_C ? weight_type : input_type));
362
+ TORCH_CHECK(dout.scalar_type() == input_type);
363
+
364
+ TORCH_CHECK(u.is_cuda());
365
+ TORCH_CHECK(delta.is_cuda());
366
+ TORCH_CHECK(A.is_cuda());
367
+ TORCH_CHECK(B.is_cuda());
368
+ TORCH_CHECK(C.is_cuda());
369
+ TORCH_CHECK(dout.is_cuda());
370
+
371
+ TORCH_CHECK(u.stride(-1) == 1 || u.size(-1) == 1);
372
+ TORCH_CHECK(delta.stride(-1) == 1 || delta.size(-1) == 1);
373
+ TORCH_CHECK(dout.stride(-1) == 1 || dout.size(-1) == 1);
374
+
375
+ const auto sizes = u.sizes();
376
+ const int batch_size = sizes[0];
377
+ const int dim = sizes[1];
378
+ const int seqlen = sizes[2];
379
+ const int dstate = A.size(1);
380
+ const int n_groups = is_variable_B ? B.size(1) : 1;
381
+
382
+ TORCH_CHECK(dstate <= 256, "selective_scan only supports state dimension <= 256");
383
+
384
+ CHECK_SHAPE(u, batch_size, dim, seqlen);
385
+ CHECK_SHAPE(delta, batch_size, dim, seqlen);
386
+ CHECK_SHAPE(A, dim, dstate);
387
+ if (!is_variable_B) {
388
+ CHECK_SHAPE(B, dim, dstate);
389
+ } else {
390
+ CHECK_SHAPE(B, batch_size, n_groups, dstate, !is_complex ? seqlen : seqlen * 2);
391
+ TORCH_CHECK(B.stride(-1) == 1 || B.size(-1) == 1);
392
+ }
393
+ if (!is_variable_C) {
394
+ CHECK_SHAPE(C, dim, dstate);
395
+ } else {
396
+ CHECK_SHAPE(C, batch_size, n_groups, dstate, !is_complex ? seqlen: seqlen * 2);
397
+ TORCH_CHECK(C.stride(-1) == 1 || C.size(-1) == 1);
398
+ }
399
+ CHECK_SHAPE(dout, batch_size, dim, seqlen);
400
+
401
+ if (D_.has_value()) {
402
+ auto D = D_.value();
403
+ TORCH_CHECK(D.scalar_type() == at::ScalarType::Float);
404
+ TORCH_CHECK(D.is_cuda());
405
+ TORCH_CHECK(D.stride(-1) == 1 || D.size(-1) == 1);
406
+ CHECK_SHAPE(D, dim);
407
+ }
408
+
409
+ if (delta_bias_.has_value()) {
410
+ auto delta_bias = delta_bias_.value();
411
+ TORCH_CHECK(delta_bias.scalar_type() == at::ScalarType::Float);
412
+ TORCH_CHECK(delta_bias.is_cuda());
413
+ TORCH_CHECK(delta_bias.stride(-1) == 1 || delta_bias.size(-1) == 1);
414
+ CHECK_SHAPE(delta_bias, dim);
415
+ }
416
+
417
+ at::Tensor z, out, dz, out_z;
418
+ const bool has_z = z_.has_value();
419
+ if (has_z) {
420
+ z = z_.value();
421
+ TORCH_CHECK(z.scalar_type() == input_type);
422
+ TORCH_CHECK(z.is_cuda());
423
+ TORCH_CHECK(z.stride(-1) == 1 || z.size(-1) == 1);
424
+ CHECK_SHAPE(z, batch_size, dim, seqlen);
425
+
426
+ TORCH_CHECK(out_.has_value());
427
+ out = out_.value();
428
+ TORCH_CHECK(out.scalar_type() == input_type);
429
+ TORCH_CHECK(out.is_cuda());
430
+ TORCH_CHECK(out.stride(-1) == 1 || out.size(-1) == 1);
431
+ CHECK_SHAPE(out, batch_size, dim, seqlen);
432
+
433
+ if (dz_.has_value()) {
434
+ dz = dz_.value();
435
+ TORCH_CHECK(dz.scalar_type() == input_type);
436
+ TORCH_CHECK(dz.is_cuda());
437
+ TORCH_CHECK(dz.stride(-1) == 1 || dz.size(-1) == 1);
438
+ CHECK_SHAPE(dz, batch_size, dim, seqlen);
439
+ } else {
440
+ dz = torch::empty_like(z);
441
+ }
442
+ if (recompute_out_z) {
443
+ out_z = torch::empty_like(out);
444
+ }
445
+ }
446
+
447
+ const int n_chunks = (seqlen + 2048 - 1) / 2048;
448
+ // const int n_chunks = (seqlen + 1024 - 1) / 1024;
449
+ if (n_chunks > 1) { TORCH_CHECK(x_.has_value()); }
450
+ if (x_.has_value()) {
451
+ auto x = x_.value();
452
+ TORCH_CHECK(x.scalar_type() == weight_type);
453
+ TORCH_CHECK(x.is_cuda());
454
+ TORCH_CHECK(x.is_contiguous());
455
+ CHECK_SHAPE(x, batch_size, dim, n_chunks, 2 * dstate);
456
+ }
457
+
458
+ at::Tensor du = torch::empty_like(u);
459
+ at::Tensor ddelta = torch::empty_like(delta);
460
+ at::Tensor dA = torch::zeros_like(A);
461
+ at::Tensor dB = !is_variable_B ? torch::zeros_like(B) : torch::zeros_like(B, B.options().dtype(torch::kFloat32));
462
+ at::Tensor dC = !is_variable_C ? torch::zeros_like(C) : torch::zeros_like(C, C.options().dtype(torch::kFloat32));
463
+ at::Tensor dD;
464
+ if (D_.has_value()) { dD = torch::zeros_like(D_.value()); }
465
+ at::Tensor ddelta_bias;
466
+ if (delta_bias_.has_value()) { ddelta_bias = torch::zeros_like(delta_bias_.value()); }
467
+
468
+ SSMParamsBwd params;
469
+ set_ssm_params_bwd(params, batch_size, dim, seqlen, dstate, n_groups, n_chunks, is_variable_B, is_variable_C,
470
+ u, delta, A, B, C, z, out, out_z,
471
+ D_.has_value() ? D_.value().data_ptr() : nullptr,
472
+ delta_bias_.has_value() ? delta_bias_.value().data_ptr() : nullptr,
473
+ x_.has_value() ? x_.value().data_ptr() : nullptr,
474
+ dout, du, ddelta, dA, dB, dC, dz,
475
+ D_.has_value() ? dD.data_ptr() : nullptr,
476
+ delta_bias_.has_value() ? ddelta_bias.data_ptr() : nullptr,
477
+ has_z, delta_softplus, recompute_out_z);
478
+
479
+ // Otherwise the kernel will be launched from cuda:0 device
480
+ // Cast to char to avoid compiler warning about narrowing
481
+ at::cuda::CUDAGuard device_guard{u.device()};
482
+ auto stream = at::cuda::getCurrentCUDAStream().stream();
483
+ DISPATCH_ITYPE_FLOAT_AND_HALF_AND_BF16(u.scalar_type(), "selective_scan_bwd", [&] {
484
+ DISPATCH_WTYPE_FLOAT_AND_COMPLEX(A.scalar_type(), "selective_scan_bwd", [&] {
485
+ selective_scan_bwd_cuda<input_t, weight_t>(params, stream);
486
+ });
487
+ });
488
+ std::vector<at::Tensor> result = {du, ddelta, dA, dB.to(B.dtype()), dC.to(C.dtype()), dD, ddelta_bias};
489
+ if (has_z) { result.push_back(dz); }
490
+ if (recompute_out_z) { result.push_back(out_z); }
491
+ return result;
492
+ }
493
+
494
+ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
495
+ m.def("fwd", &selective_scan_fwd, "Selective scan forward");
496
+ m.def("bwd", &selective_scan_bwd, "Selective scan backward");
497
+ }
vendor/mamba-og/csrc/selective_scan/selective_scan.h ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ #pragma once
6
+
7
+ struct SSMParamsBase {
8
+ using index_t = uint64_t;
9
+
10
+ int batch, dim, seqlen, dstate, n_groups, n_chunks;
11
+ int dim_ngroups_ratio;
12
+ bool is_variable_B;
13
+ bool is_variable_C;
14
+
15
+ bool delta_softplus;
16
+
17
+ index_t A_d_stride;
18
+ index_t A_dstate_stride;
19
+ index_t B_batch_stride;
20
+ index_t B_d_stride;
21
+ index_t B_dstate_stride;
22
+ index_t B_group_stride;
23
+ index_t C_batch_stride;
24
+ index_t C_d_stride;
25
+ index_t C_dstate_stride;
26
+ index_t C_group_stride;
27
+ index_t u_batch_stride;
28
+ index_t u_d_stride;
29
+ index_t delta_batch_stride;
30
+ index_t delta_d_stride;
31
+ index_t z_batch_stride;
32
+ index_t z_d_stride;
33
+ index_t out_batch_stride;
34
+ index_t out_d_stride;
35
+ index_t out_z_batch_stride;
36
+ index_t out_z_d_stride;
37
+
38
+ // Common data pointers.
39
+ void *__restrict__ A_ptr;
40
+ void *__restrict__ B_ptr;
41
+ void *__restrict__ C_ptr;
42
+ void *__restrict__ D_ptr;
43
+ void *__restrict__ u_ptr;
44
+ void *__restrict__ delta_ptr;
45
+ void *__restrict__ delta_bias_ptr;
46
+ void *__restrict__ out_ptr;
47
+ void *__restrict__ x_ptr;
48
+ void *__restrict__ z_ptr;
49
+ void *__restrict__ out_z_ptr;
50
+ };
51
+
52
+ struct SSMParamsBwd: public SSMParamsBase {
53
+ index_t dout_batch_stride;
54
+ index_t dout_d_stride;
55
+ index_t dA_d_stride;
56
+ index_t dA_dstate_stride;
57
+ index_t dB_batch_stride;
58
+ index_t dB_group_stride;
59
+ index_t dB_d_stride;
60
+ index_t dB_dstate_stride;
61
+ index_t dC_batch_stride;
62
+ index_t dC_group_stride;
63
+ index_t dC_d_stride;
64
+ index_t dC_dstate_stride;
65
+ index_t du_batch_stride;
66
+ index_t du_d_stride;
67
+ index_t dz_batch_stride;
68
+ index_t dz_d_stride;
69
+ index_t ddelta_batch_stride;
70
+ index_t ddelta_d_stride;
71
+
72
+ // Common data pointers.
73
+ void *__restrict__ dout_ptr;
74
+ void *__restrict__ dA_ptr;
75
+ void *__restrict__ dB_ptr;
76
+ void *__restrict__ dC_ptr;
77
+ void *__restrict__ dD_ptr;
78
+ void *__restrict__ du_ptr;
79
+ void *__restrict__ dz_ptr;
80
+ void *__restrict__ ddelta_ptr;
81
+ void *__restrict__ ddelta_bias_ptr;
82
+ };
vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_bf16_complex.cu ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ // Split into multiple files to compile in paralell
6
+
7
+ #include "selective_scan_bwd_kernel.cuh"
8
+
9
+ template void selective_scan_bwd_cuda<at::BFloat16, complex_t>(SSMParamsBwd &params, cudaStream_t stream);
vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_bf16_real.cu ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ // Split into multiple files to compile in paralell
6
+
7
+ #include "selective_scan_bwd_kernel.cuh"
8
+
9
+ template void selective_scan_bwd_cuda<at::BFloat16, float>(SSMParamsBwd &params, cudaStream_t stream);
vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_fp16_complex.cu ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ // Split into multiple files to compile in paralell
6
+
7
+ #include "selective_scan_bwd_kernel.cuh"
8
+
9
+ template void selective_scan_bwd_cuda<at::Half, complex_t>(SSMParamsBwd &params, cudaStream_t stream);
vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_fp16_real.cu ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ // Split into multiple files to compile in paralell
6
+
7
+ #include "selective_scan_bwd_kernel.cuh"
8
+
9
+ template void selective_scan_bwd_cuda<at::Half, float>(SSMParamsBwd &params, cudaStream_t stream);
vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_fp32_complex.cu ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ // Split into multiple files to compile in paralell
6
+
7
+ #include "selective_scan_bwd_kernel.cuh"
8
+
9
+ template void selective_scan_bwd_cuda<float, complex_t>(SSMParamsBwd &params, cudaStream_t stream);
vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_fp32_real.cu ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ // Split into multiple files to compile in paralell
6
+
7
+ #include "selective_scan_bwd_kernel.cuh"
8
+
9
+ template void selective_scan_bwd_cuda<float, float>(SSMParamsBwd &params, cudaStream_t stream);
vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_kernel.cuh ADDED
@@ -0,0 +1,561 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ #pragma once
6
+
7
+ #include <c10/util/BFloat16.h>
8
+ #include <c10/util/Half.h>
9
+ #include <c10/cuda/CUDAException.h> // For C10_CUDA_CHECK and C10_CUDA_KERNEL_LAUNCH_CHECK
10
+ #include <ATen/cuda/Atomic.cuh> // For atomicAdd on complex
11
+
12
+ #ifndef USE_ROCM
13
+ #include <cub/block/block_load.cuh>
14
+ #include <cub/block/block_store.cuh>
15
+ #include <cub/block/block_scan.cuh>
16
+ #include <cub/block/block_reduce.cuh>
17
+ #else
18
+ #include <hipcub/hipcub.hpp>
19
+ namespace cub = hipcub;
20
+ #endif
21
+
22
+ #include "selective_scan.h"
23
+ #include "selective_scan_common.h"
24
+ #include "reverse_scan.cuh"
25
+ #include "static_switch.h"
26
+
27
+ template<typename scalar_t> __device__ __forceinline__ scalar_t conj(scalar_t x);
28
+ template<> __device__ __forceinline__ float conj<float>(float x) { return x; }
29
+ template<> __device__ __forceinline__ complex_t conj<complex_t>(complex_t x) { return std::conj(x); }
30
+
31
+ template<int kNThreads_, int kNItems_, bool kIsEvenLen_, bool kIsVariableB_, bool kIsVariableC_,
32
+ bool kDeltaSoftplus_, bool kHasZ_, typename input_t_, typename weight_t_>
33
+ struct Selective_Scan_bwd_kernel_traits {
34
+ static_assert(kNItems_ % 4 == 0);
35
+ using input_t = input_t_;
36
+ using weight_t = weight_t_;
37
+ static constexpr int kNThreads = kNThreads_;
38
+ static constexpr int kNItems = kNItems_;
39
+ static constexpr int kNBytes = sizeof(input_t);
40
+ static_assert(kNBytes == 2 || kNBytes == 4);
41
+ static constexpr int kNElts = kNBytes == 4 ? 4 : constexpr_min(8, kNItems);
42
+ static_assert(kNItems % kNElts == 0);
43
+ static constexpr int kNLoads = kNItems / kNElts;
44
+ static constexpr bool kIsComplex = std::is_same_v<weight_t, complex_t>;
45
+ static constexpr bool kIsEvenLen = kIsEvenLen_;
46
+ static constexpr bool kIsVariableB = kIsVariableB_;
47
+ static constexpr bool kIsVariableC = kIsVariableC_;
48
+ static constexpr bool kDeltaSoftplus = kDeltaSoftplus_;
49
+ static constexpr bool kHasZ = kHasZ_;
50
+ // Setting MinBlocksPerMP to be 3 (instead of 2) for 128 threads with float improves occupancy.
51
+ // For complex this would lead to massive register spilling, so we keep it at 2.
52
+ static constexpr int kMinBlocks = kNThreads == 128 && !kIsComplex ? 3 : 2;
53
+ using vec_t = typename BytesToType<kNBytes * kNElts>::Type;
54
+ using scan_t = std::conditional_t<!kIsComplex, float2, float4>;
55
+ using BlockLoadT = cub::BlockLoad<input_t, kNThreads, kNItems, cub::BLOCK_LOAD_WARP_TRANSPOSE>;
56
+ using BlockLoadVecT = cub::BlockLoad<vec_t, kNThreads, kNLoads, cub::BLOCK_LOAD_WARP_TRANSPOSE>;
57
+ using BlockLoadWeightT = cub::BlockLoad<input_t, kNThreads, !kIsComplex ? kNItems : kNItems * 2, cub::BLOCK_LOAD_WARP_TRANSPOSE>;
58
+ using BlockLoadWeightVecT = cub::BlockLoad<vec_t, kNThreads, !kIsComplex ? kNLoads : kNLoads * 2, cub::BLOCK_LOAD_WARP_TRANSPOSE>;
59
+ using BlockStoreT = cub::BlockStore<input_t, kNThreads, kNItems, cub::BLOCK_STORE_WARP_TRANSPOSE>;
60
+ using BlockStoreVecT = cub::BlockStore<vec_t, kNThreads, kNLoads, cub::BLOCK_STORE_WARP_TRANSPOSE>;
61
+ // using BlockScanT = cub::BlockScan<scan_t, kNThreads, cub::BLOCK_SCAN_RAKING_MEMOIZE>;
62
+ using BlockScanT = cub::BlockScan<scan_t, kNThreads, cub::BLOCK_SCAN_RAKING>;
63
+ // using BlockScanT = cub::BlockScan<scan_t, kNThreads, cub::BLOCK_SCAN_WARP_SCANS>;
64
+ using BlockReverseScanT = BlockReverseScan<scan_t, kNThreads>;
65
+ using BlockReduceT = cub::BlockReduce<scan_t, kNThreads>;
66
+ using BlockReduceFloatT = cub::BlockReduce<float, kNThreads>;
67
+ using BlockReduceComplexT = cub::BlockReduce<complex_t, kNThreads>;
68
+ using BlockExchangeT = cub::BlockExchange<float, kNThreads, !kIsComplex ? kNItems : kNItems * 2>;
69
+
70
+ static constexpr int kSmemIOSize = custom_max({sizeof(typename BlockLoadT::TempStorage),
71
+ sizeof(typename BlockLoadVecT::TempStorage),
72
+ (int(kIsVariableB) + int(kIsVariableC)) * sizeof(typename BlockLoadWeightT::TempStorage),
73
+ (int(kIsVariableB) + int(kIsVariableC)) * sizeof(typename BlockLoadWeightVecT::TempStorage),
74
+ sizeof(typename BlockStoreT::TempStorage),
75
+ sizeof(typename BlockStoreVecT::TempStorage)});
76
+ static constexpr int kSmemExchangeSize = (int(kIsVariableB) + int(kIsVariableC)) * sizeof(typename BlockExchangeT::TempStorage);
77
+ static constexpr int kSmemReduceSize = sizeof(typename BlockReduceT::TempStorage);
78
+ static constexpr int kSmemSize = kSmemIOSize + kSmemExchangeSize + kSmemReduceSize + sizeof(typename BlockScanT::TempStorage) + sizeof(typename BlockReverseScanT::TempStorage);
79
+ };
80
+
81
+ template<typename Ktraits>
82
+ __global__ __launch_bounds__(Ktraits::kNThreads, Ktraits::kMinBlocks)
83
+ void selective_scan_bwd_kernel(SSMParamsBwd params) {
84
+ constexpr bool kIsComplex = Ktraits::kIsComplex;
85
+ constexpr bool kIsVariableB = Ktraits::kIsVariableB;
86
+ constexpr bool kIsVariableC = Ktraits::kIsVariableC;
87
+ constexpr bool kDeltaSoftplus = Ktraits::kDeltaSoftplus;
88
+ constexpr bool kHasZ = Ktraits::kHasZ;
89
+ constexpr int kNThreads = Ktraits::kNThreads;
90
+ constexpr int kNItems = Ktraits::kNItems;
91
+ using input_t = typename Ktraits::input_t;
92
+ using weight_t = typename Ktraits::weight_t;
93
+ using scan_t = typename Ktraits::scan_t;
94
+
95
+ // Shared memory.
96
+ extern __shared__ char smem_[];
97
+ // cast to lvalue reference of expected type
98
+ // char *smem_loadstorescan = smem_ + 2 * MAX_DSTATE * sizeof(weight_t);
99
+ // auto& smem_load = reinterpret_cast<typename BlockLoadT::TempStorage&>(smem_ + 2 * MAX_DSTATE * sizeof(weight_t));
100
+ // auto& smem_load = reinterpret_cast<typename BlockLoadT::TempStorage&>(smem_loadstorescan);
101
+ auto& smem_load = reinterpret_cast<typename Ktraits::BlockLoadT::TempStorage&>(smem_);
102
+ auto& smem_load_weight = reinterpret_cast<typename Ktraits::BlockLoadWeightT::TempStorage&>(smem_);
103
+ auto& smem_load_weight1 = *reinterpret_cast<typename Ktraits::BlockLoadWeightT::TempStorage*>(smem_ + sizeof(typename Ktraits::BlockLoadWeightT::TempStorage));
104
+ auto& smem_store = reinterpret_cast<typename Ktraits::BlockStoreT::TempStorage&>(smem_);
105
+ auto& smem_exchange = *reinterpret_cast<typename Ktraits::BlockExchangeT::TempStorage*>(smem_ + Ktraits::kSmemIOSize);
106
+ auto& smem_exchange1 = *reinterpret_cast<typename Ktraits::BlockExchangeT::TempStorage*>(smem_ + Ktraits::kSmemIOSize + sizeof(typename Ktraits::BlockExchangeT::TempStorage));
107
+ auto& smem_reduce = *reinterpret_cast<typename Ktraits::BlockReduceT::TempStorage*>(reinterpret_cast<char *>(&smem_exchange) + Ktraits::kSmemExchangeSize);
108
+ auto& smem_reduce_float = *reinterpret_cast<typename Ktraits::BlockReduceFloatT::TempStorage*>(&smem_reduce);
109
+ auto& smem_reduce_complex = *reinterpret_cast<typename Ktraits::BlockReduceComplexT::TempStorage*>(&smem_reduce);
110
+ auto& smem_scan = *reinterpret_cast<typename Ktraits::BlockScanT::TempStorage*>(reinterpret_cast<char *>(&smem_reduce) + Ktraits::kSmemReduceSize);
111
+ auto& smem_reverse_scan = *reinterpret_cast<typename Ktraits::BlockReverseScanT::TempStorage*>(reinterpret_cast<char *>(&smem_scan) + sizeof(typename Ktraits::BlockScanT::TempStorage));
112
+ weight_t *smem_delta_a = reinterpret_cast<weight_t *>(smem_ + Ktraits::kSmemSize);
113
+ scan_t *smem_running_postfix = reinterpret_cast<scan_t *>(smem_delta_a + 2 * MAX_DSTATE + kNThreads);
114
+ weight_t *smem_da = reinterpret_cast<weight_t *>(smem_running_postfix + MAX_DSTATE);
115
+ weight_t *smem_dbc = reinterpret_cast<weight_t *>(smem_da + MAX_DSTATE);
116
+
117
+ const int batch_id = blockIdx.x;
118
+ const int dim_id = blockIdx.y;
119
+ const int group_id = dim_id / (params.dim_ngroups_ratio);
120
+ input_t *u = reinterpret_cast<input_t *>(params.u_ptr) + batch_id * params.u_batch_stride
121
+ + dim_id * params.u_d_stride;
122
+ input_t *delta = reinterpret_cast<input_t *>(params.delta_ptr) + batch_id * params.delta_batch_stride
123
+ + dim_id * params.delta_d_stride;
124
+ input_t *dout = reinterpret_cast<input_t *>(params.dout_ptr) + batch_id * params.dout_batch_stride
125
+ + dim_id * params.dout_d_stride;
126
+ weight_t *A = reinterpret_cast<weight_t *>(params.A_ptr) + dim_id * params.A_d_stride;
127
+ weight_t *B = reinterpret_cast<weight_t *>(params.B_ptr) + dim_id * params.B_d_stride;
128
+ input_t *Bvar = reinterpret_cast<input_t *>(params.B_ptr) + batch_id * params.B_batch_stride + group_id * params.B_group_stride;
129
+ weight_t *C = reinterpret_cast<weight_t *>(params.C_ptr) + dim_id * params.C_d_stride;
130
+ input_t *Cvar = reinterpret_cast<input_t *>(params.C_ptr) + batch_id * params.C_batch_stride + group_id * params.C_group_stride;
131
+ weight_t *dA = reinterpret_cast<weight_t *>(params.dA_ptr) + dim_id * params.dA_d_stride;
132
+ weight_t *dB = reinterpret_cast<weight_t *>(params.dB_ptr)
133
+ + (!kIsVariableB ? dim_id * params.dB_d_stride : batch_id * (!kIsComplex ? params.dB_batch_stride : params.dB_batch_stride / 2) + group_id * params.dB_group_stride);
134
+ weight_t *dC = reinterpret_cast<weight_t *>(params.dC_ptr)
135
+ + (!kIsVariableC ? dim_id * params.dC_d_stride : batch_id * (!kIsComplex ? params.dC_batch_stride : params.dC_batch_stride / 2) + group_id * params.dC_group_stride);
136
+ float *dD = params.dD_ptr == nullptr ? nullptr : reinterpret_cast<float *>(params.dD_ptr) + dim_id;
137
+ float D_val = params.D_ptr == nullptr ? 0 : reinterpret_cast<float *>(params.D_ptr)[dim_id];
138
+ float *ddelta_bias = params.ddelta_bias_ptr == nullptr ? nullptr : reinterpret_cast<float *>(params.ddelta_bias_ptr) + dim_id;
139
+ float delta_bias = params.delta_bias_ptr == nullptr ? 0 : reinterpret_cast<float *>(params.delta_bias_ptr)[dim_id];
140
+ scan_t *x = params.x_ptr == nullptr
141
+ ? nullptr
142
+ : reinterpret_cast<scan_t *>(params.x_ptr) + (batch_id * params.dim + dim_id) * (params.n_chunks) * params.dstate;
143
+ float dD_val = 0;
144
+ float ddelta_bias_val = 0;
145
+
146
+ constexpr int kChunkSize = kNThreads * kNItems;
147
+ u += (params.n_chunks - 1) * kChunkSize;
148
+ delta += (params.n_chunks - 1) * kChunkSize;
149
+ dout += (params.n_chunks - 1) * kChunkSize;
150
+ Bvar += (params.n_chunks - 1) * kChunkSize * (!kIsComplex ? 1 : 2);
151
+ Cvar += (params.n_chunks - 1) * kChunkSize * (!kIsComplex ? 1 : 2);
152
+ for (int chunk = params.n_chunks - 1; chunk >= 0; --chunk) {
153
+ input_t u_vals[kNItems];
154
+ input_t delta_vals_load[kNItems];
155
+ input_t dout_vals_load[kNItems];
156
+ __syncthreads();
157
+ load_input<Ktraits>(u, u_vals, smem_load, params.seqlen - chunk * kChunkSize);
158
+ u -= kChunkSize;
159
+ __syncthreads();
160
+ load_input<Ktraits>(delta, delta_vals_load, smem_load, params.seqlen - chunk * kChunkSize);
161
+ // Will reload delta at the same location if kDeltaSoftplus
162
+ if constexpr (!kDeltaSoftplus) { delta -= kChunkSize; }
163
+ __syncthreads();
164
+ load_input<Ktraits>(dout, dout_vals_load, smem_load, params.seqlen - chunk * kChunkSize);
165
+ dout -= kChunkSize;
166
+
167
+ float dout_vals[kNItems], delta_vals[kNItems];
168
+ #pragma unroll
169
+ for (int i = 0; i < kNItems; ++i) {
170
+ dout_vals[i] = float(dout_vals_load[i]);
171
+ delta_vals[i] = float(delta_vals_load[i]) + delta_bias;
172
+ if constexpr (kDeltaSoftplus) {
173
+ delta_vals[i] = delta_vals[i] <= 20.f ? log1pf(expf(delta_vals[i])) : delta_vals[i];
174
+ }
175
+ }
176
+
177
+ if constexpr (kHasZ) {
178
+ input_t *z = reinterpret_cast<input_t *>(params.z_ptr) + batch_id * params.z_batch_stride
179
+ + dim_id * params.z_d_stride + chunk * kChunkSize;
180
+ input_t *out = reinterpret_cast<input_t *>(params.out_ptr) + batch_id * params.out_batch_stride
181
+ + dim_id * params.out_d_stride + chunk * kChunkSize;
182
+ input_t *dz = reinterpret_cast<input_t *>(params.dz_ptr) + batch_id * params.dz_batch_stride
183
+ + dim_id * params.dz_d_stride + chunk * kChunkSize;
184
+ input_t z_vals[kNItems], out_vals[kNItems];
185
+ __syncthreads();
186
+ load_input<Ktraits>(z, z_vals, smem_load, params.seqlen - chunk * kChunkSize);
187
+ __syncthreads();
188
+ load_input<Ktraits>(out, out_vals, smem_load, params.seqlen - chunk * kChunkSize);
189
+ float dz_vals[kNItems], z_silu_vals[kNItems];
190
+ #pragma unroll
191
+ for (int i = 0; i < kNItems; ++i) {
192
+ float z_val = z_vals[i];
193
+ float z_sigmoid_val = 1.0f / (1.0f + expf(-z_val));
194
+ z_silu_vals[i] = z_val * z_sigmoid_val;
195
+ dz_vals[i] = dout_vals[i] * float(out_vals[i]) * z_sigmoid_val
196
+ * (1.0f + z_val * (1.0f - z_sigmoid_val));
197
+ dout_vals[i] *= z_silu_vals[i];
198
+ }
199
+ __syncthreads();
200
+ store_output<Ktraits>(dz, dz_vals, smem_store, params.seqlen - chunk * kChunkSize);
201
+ if (params.out_z_ptr != nullptr) { // Recompute and store out_z
202
+ float out_z_vals[kNItems];
203
+ #pragma unroll
204
+ for (int i = 0; i < kNItems; ++i) { out_z_vals[i] = float(out_vals[i]) * z_silu_vals[i]; }
205
+ // if (blockIdx.x == 0 && blockIdx.y == 0 && threadIdx.x == 0) {
206
+ // printf("out_val=%f, z_silu_val = %f, out_z_val = %f\n", float(out_vals[0]), z_silu_vals[0], out_z_vals[0]);
207
+ // }
208
+ input_t *out_z = reinterpret_cast<input_t *>(params.out_z_ptr) + batch_id * params.out_z_batch_stride
209
+ + dim_id * params.out_z_d_stride + chunk * kChunkSize;
210
+ __syncthreads();
211
+ store_output<Ktraits>(out_z, out_z_vals, smem_store, params.seqlen - chunk * kChunkSize);
212
+ }
213
+ }
214
+
215
+ float du_vals[kNItems];
216
+ #pragma unroll
217
+ for (int i = 0; i < kNItems; ++i) { du_vals[i] = D_val * dout_vals[i]; }
218
+ #pragma unroll
219
+ for (int i = 0; i < kNItems; ++i) { dD_val += dout_vals[i] * float(u_vals[i]); }
220
+
221
+ float ddelta_vals[kNItems] = {0};
222
+ __syncthreads();
223
+ for (int state_idx = 0; state_idx < params.dstate; ++state_idx) {
224
+ const weight_t A_val = A[state_idx * params.A_dstate_stride];
225
+ // Multiply the real part of A with LOG2E so we can use exp2f instead of expf.
226
+ weight_t A_scaled;
227
+ constexpr float kLog2e = M_LOG2E;
228
+ if constexpr (!kIsComplex) {
229
+ A_scaled = A_val * kLog2e;
230
+ } else {
231
+ A_scaled = complex_t(A_val.real_ * kLog2e, A_val.imag_);
232
+ }
233
+ weight_t B_val, C_val;
234
+ weight_t B_vals[kNItems], C_vals[kNItems];
235
+ if constexpr (!kIsVariableB) {
236
+ B_val = B[state_idx * params.B_dstate_stride];
237
+ } else {
238
+ load_weight<Ktraits>(Bvar + state_idx * params.B_dstate_stride, B_vals,
239
+ smem_load_weight, (params.seqlen - chunk * kChunkSize) * (!kIsComplex ? 1 : 2));
240
+ }
241
+ if constexpr (!kIsVariableC) {
242
+ C_val = C[state_idx * params.C_dstate_stride];
243
+ } else {
244
+ auto &smem_load_weight_C = !kIsVariableB ? smem_load_weight : smem_load_weight1;
245
+ load_weight<Ktraits>(Cvar + state_idx * params.C_dstate_stride, C_vals,
246
+ smem_load_weight_C, (params.seqlen - chunk * kChunkSize) * (!kIsComplex ? 1 : 2));
247
+ }
248
+ // const weight_t A_val = smem_a[state_idx];
249
+ scan_t thread_data[kNItems], thread_reverse_data[kNItems];
250
+ if constexpr (!kIsComplex) {
251
+ #pragma unroll
252
+ for (int i = 0; i < kNItems; ++i) {
253
+ const float delta_a_exp = exp2f(delta_vals[i] * A_scaled);
254
+ thread_data[i] = make_float2(delta_a_exp, !kIsVariableB ? delta_vals[i] * float(u_vals[i]) : delta_vals[i] * float(u_vals[i]) * B_vals[i]);
255
+ if (i == 0) {
256
+ smem_delta_a[threadIdx.x == 0 ? state_idx + (chunk % 2) * MAX_DSTATE : threadIdx.x + 2 * MAX_DSTATE] = delta_a_exp;
257
+ } else {
258
+ thread_reverse_data[i - 1].x = delta_a_exp;
259
+ }
260
+ thread_reverse_data[i].y = dout_vals[i] *
261
+ (!kIsVariableC
262
+ ? (!kIsVariableB ? B_val * C_val : C_val)
263
+ : (!kIsVariableB ? B_val * C_vals[i] : C_vals[i]));
264
+ }
265
+ __syncthreads();
266
+ thread_reverse_data[kNItems - 1].x = threadIdx.x == kNThreads - 1
267
+ ? (chunk == params.n_chunks - 1 ? 1.f : smem_delta_a[state_idx + ((chunk + 1) % 2) * MAX_DSTATE])
268
+ : smem_delta_a[threadIdx.x + 1 + 2 * MAX_DSTATE];
269
+ // Initialize running total
270
+ scan_t running_prefix = chunk > 0 && threadIdx.x % 32 == 0 ? x[(chunk - 1) * params.dstate + state_idx] : make_float2(1.f, 0.f);
271
+ SSMScanPrefixCallbackOp<weight_t> prefix_op(running_prefix);
272
+ typename Ktraits::BlockScanT(smem_scan).InclusiveScan(
273
+ thread_data, thread_data, SSMScanOp<weight_t>(), prefix_op
274
+ );
275
+ scan_t running_postfix = chunk < params.n_chunks - 1 && threadIdx.x % 32 == 0 ? smem_running_postfix[state_idx] : make_float2(1.f, 0.f);
276
+ SSMScanPrefixCallbackOp<weight_t> postfix_op(running_postfix);
277
+ typename Ktraits::BlockReverseScanT(smem_reverse_scan).InclusiveReverseScan(
278
+ thread_reverse_data, thread_reverse_data, SSMScanOp<weight_t>(), postfix_op
279
+ );
280
+ if (threadIdx.x == 0) { smem_running_postfix[state_idx] = postfix_op.running_prefix; }
281
+ weight_t dA_val = 0, dBC_val = 0;
282
+ weight_t dB_vals[kNItems], dC_vals[kNItems];
283
+ #pragma unroll
284
+ for (int i = 0; i < kNItems; ++i) {
285
+ const float dx = thread_reverse_data[i].y;
286
+ const float ddelta_u = !kIsVariableB ? dx : dx * B_vals[i];
287
+ du_vals[i] += ddelta_u * delta_vals[i];
288
+ const float a = thread_data[i].y - (!kIsVariableB ? delta_vals[i] * float(u_vals[i]) : delta_vals[i] * float(u_vals[i]) * B_vals[i]);
289
+ ddelta_vals[i] += ddelta_u * float(u_vals[i]) + dx * A_val * a;
290
+ dA_val += dx * delta_vals[i] * a;
291
+ if constexpr (!kIsVariableB || !kIsVariableC) {
292
+ if constexpr (!kIsVariableB) { // dBC_val is dB_val
293
+ dBC_val += dout_vals[i] * (!kIsVariableC ? thread_data[i].y : thread_data[i].y * C_vals[i]);
294
+ } else { // dBC_val is dC_val
295
+ dBC_val += dout_vals[i] * thread_data[i].y;
296
+ }
297
+ }
298
+ if constexpr (kIsVariableB) { dB_vals[i] = dx * delta_vals[i] * float(u_vals[i]); }
299
+ if constexpr (kIsVariableC) {
300
+ dC_vals[i] = dout_vals[i] * (!kIsVariableB ? thread_data[i].y * B_val : thread_data[i].y);
301
+ }
302
+ }
303
+ // Block-exchange to make the atomicAdd's coalesced, otherwise they're much slower
304
+ if constexpr (kIsVariableB || kIsVariableC) {
305
+ if constexpr (kIsVariableB) {
306
+ typename Ktraits::BlockExchangeT(smem_exchange).BlockedToStriped(dB_vals, dB_vals);
307
+ }
308
+ if constexpr (kIsVariableC) {
309
+ auto &smem_exchange_C = !kIsVariableB ? smem_exchange : smem_exchange1;
310
+ typename Ktraits::BlockExchangeT(smem_exchange_C).BlockedToStriped(dC_vals, dC_vals);
311
+ }
312
+ const int seqlen_remaining = params.seqlen - chunk * kChunkSize - threadIdx.x;
313
+ weight_t *dB_cur = dB + state_idx * params.dB_dstate_stride + chunk * kChunkSize + threadIdx.x;
314
+ weight_t *dC_cur = dC + state_idx * params.dC_dstate_stride + chunk * kChunkSize + threadIdx.x;
315
+ #pragma unroll
316
+ for (int i = 0; i < kNItems; ++i) {
317
+ if (i * kNThreads < seqlen_remaining) {
318
+ if constexpr (kIsVariableB) { gpuAtomicAdd(dB_cur + i * kNThreads, dB_vals[i]); }
319
+ if constexpr (kIsVariableC) { gpuAtomicAdd(dC_cur + i * kNThreads, dC_vals[i]); }
320
+ }
321
+ }
322
+ }
323
+ if constexpr (!kIsVariableB || !kIsVariableC) {
324
+ float2 dA_dBC_val = make_float2(dA_val, dBC_val);
325
+ dA_dBC_val = typename Ktraits::BlockReduceT(smem_reduce).Sum(dA_dBC_val);
326
+ dA_val = dA_dBC_val.x;
327
+ if (threadIdx.x == 0) {
328
+ smem_dbc[state_idx] = chunk == params.n_chunks - 1 ? dA_dBC_val.y : dA_dBC_val.y + smem_dbc[state_idx];
329
+ }
330
+ } else {
331
+ dA_val = typename Ktraits::BlockReduceFloatT(smem_reduce_float).Sum(dA_val);
332
+ }
333
+ if (threadIdx.x == 0) {
334
+ smem_da[state_idx] = chunk == params.n_chunks - 1 ? dA_val : dA_val + smem_da[state_idx];
335
+ }
336
+ } else {
337
+ #pragma unroll
338
+ for (int i = 0; i < kNItems; ++i) {
339
+ // Pytorch's implementation of complex exp (which calls thrust) is very slow
340
+ complex_t delta_a_exp = cexp2f(delta_vals[i] * A_scaled);
341
+ weight_t B_delta_u_val = !kIsVariableB ? delta_vals[i] * float(u_vals[i]) : B_vals[i] * delta_vals[i] * float(u_vals[i]);
342
+ thread_data[i] = make_float4(delta_a_exp.real_, delta_a_exp.imag_, B_delta_u_val.real_, B_delta_u_val.imag_);
343
+ if (i == 0) {
344
+ smem_delta_a[threadIdx.x == 0 ? state_idx + (chunk % 2) * MAX_DSTATE : threadIdx.x + 2 * MAX_DSTATE] = delta_a_exp;
345
+ } else {
346
+ thread_reverse_data[i - 1].x = delta_a_exp.real_;
347
+ thread_reverse_data[i - 1].y = -delta_a_exp.imag_;
348
+ }
349
+ complex_t dout_BC = 2 * dout_vals[i]
350
+ * conj(!kIsVariableC
351
+ ? (!kIsVariableB ? B_val * C_val : C_val)
352
+ : (!kIsVariableB ? B_val * C_vals[i] : C_vals[i]));
353
+ thread_reverse_data[i].z = dout_BC.real_;
354
+ thread_reverse_data[i].w = dout_BC.imag_;
355
+ }
356
+ __syncthreads();
357
+ complex_t delta_a_exp = threadIdx.x == kNThreads - 1
358
+ ? (chunk == params.n_chunks - 1 ? 1.f : smem_delta_a[state_idx + ((chunk + 1) % 2) * MAX_DSTATE])
359
+ : smem_delta_a[threadIdx.x + 1 + 2 * MAX_DSTATE];
360
+ thread_reverse_data[kNItems - 1].x = delta_a_exp.real_;
361
+ thread_reverse_data[kNItems - 1].y = -delta_a_exp.imag_;
362
+ // Initialize running total
363
+ scan_t running_prefix = chunk > 0 && threadIdx.x % 32 == 0 ? x[(chunk - 1) * params.dstate + state_idx] : make_float4(1.f, 0.f, 0.f, 0.f);
364
+ SSMScanPrefixCallbackOp<weight_t> prefix_op(running_prefix);
365
+ typename Ktraits::BlockScanT(smem_scan).InclusiveScan(
366
+ thread_data, thread_data, SSMScanOp<weight_t>(), prefix_op
367
+ );
368
+ scan_t running_postfix = chunk < params.n_chunks - 1 && threadIdx.x % 32 == 0 ? smem_running_postfix[state_idx] : make_float4(1.f, 0.f, 0.f, 0.f);
369
+ SSMScanPrefixCallbackOp<weight_t> postfix_op(running_postfix);
370
+ typename Ktraits::BlockReverseScanT(smem_reverse_scan).InclusiveReverseScan(
371
+ thread_reverse_data, thread_reverse_data, SSMScanOp<weight_t>(), postfix_op
372
+ );
373
+ if (threadIdx.x == 0) { smem_running_postfix[state_idx] = postfix_op.running_prefix; }
374
+ weight_t dA_val = 0, dBC_val = 0;
375
+ weight_t dB_vals[kNItems], dC_vals[kNItems];
376
+ #pragma unroll
377
+ for (int i = 0; i < kNItems; ++i) {
378
+ complex_t x = complex_t(thread_data[i].z, thread_data[i].w);
379
+ complex_t dx = complex_t(thread_reverse_data[i].z, thread_reverse_data[i].w);
380
+ float ddelta_u = !kIsVariableB ? dx.real_ : (dx * conj(B_vals[i])).real_;
381
+ if constexpr (!kIsVariableB || !kIsVariableC) {
382
+ if constexpr (!kIsVariableB) { // dBC_val is dB_val
383
+ dBC_val += (2 * dout_vals[i]) * conj(!kIsVariableC ? x : x * C_vals[i]);
384
+ } else { // dBC_val is dC_val
385
+ dBC_val += (2 * dout_vals[i]) * conj(x);
386
+ }
387
+ }
388
+ const complex_t a_conj = conj(x - (!kIsVariableB ? delta_vals[i] * float(u_vals[i]) : delta_vals[i] * float(u_vals[i]) * B_vals[i]));
389
+ du_vals[i] += ddelta_u * delta_vals[i];
390
+ ddelta_vals[i] += ddelta_u * float(u_vals[i]) + (dx * conj(A_val) * a_conj).real_;
391
+ dA_val += delta_vals[i] * dx * a_conj;
392
+ if constexpr (kIsVariableB) { dB_vals[i] = dx * delta_vals[i] * float(u_vals[i]); }
393
+ if constexpr (kIsVariableC) {
394
+ dC_vals[i] = (2 * dout_vals[i]) * conj(!kIsVariableB ? x * B_val : x);
395
+ }
396
+ }
397
+ // Block-exchange to make the atomicAdd's coalesced, otherwise they're much slower
398
+ if constexpr (kIsVariableB || kIsVariableC) {
399
+ float dB_vals_f[kNItems * 2], dC_vals_f[kNItems * 2];
400
+ if constexpr (kIsVariableB) {
401
+ #pragma unroll
402
+ for (int i = 0; i < kNItems; ++i) {
403
+ dB_vals_f[i * 2] = dB_vals[i].real_;
404
+ dB_vals_f[i * 2 + 1] = dB_vals[i].imag_;
405
+ }
406
+ typename Ktraits::BlockExchangeT(smem_exchange).BlockedToStriped(dB_vals_f, dB_vals_f);
407
+ }
408
+ if constexpr (kIsVariableC) {
409
+ #pragma unroll
410
+ for (int i = 0; i < kNItems; ++i) {
411
+ dC_vals_f[i * 2] = dC_vals[i].real_;
412
+ dC_vals_f[i * 2 + 1] = dC_vals[i].imag_;
413
+ }
414
+ auto &smem_exchange_C = !kIsVariableB ? smem_exchange : smem_exchange1;
415
+ typename Ktraits::BlockExchangeT(smem_exchange_C).BlockedToStriped(dC_vals_f, dC_vals_f);
416
+ }
417
+ const int seqlen_remaining = (params.seqlen - chunk * kChunkSize) * 2 - threadIdx.x;
418
+ float *dB_cur = reinterpret_cast<float *>(dB) + state_idx * params.dB_dstate_stride + chunk * kChunkSize * 2 + threadIdx.x;
419
+ float *dC_cur = reinterpret_cast<float *>(dC) + state_idx * params.dC_dstate_stride + chunk * kChunkSize * 2 + threadIdx.x;
420
+ #pragma unroll
421
+ for (int i = 0; i < kNItems * 2; ++i) {
422
+ if (i * kNThreads < seqlen_remaining) {
423
+ if constexpr (kIsVariableB) { gpuAtomicAdd(dB_cur + i * kNThreads, dB_vals_f[i]); }
424
+ if constexpr (kIsVariableC) { gpuAtomicAdd(dC_cur + i * kNThreads, dC_vals_f[i]); }
425
+ }
426
+ }
427
+ }
428
+ if constexpr (!kIsVariableB || !kIsVariableC) {
429
+ float4 dA_dBC_val = make_float4(dA_val.real_, dA_val.imag_, dBC_val.real_, dBC_val.imag_);
430
+ dA_dBC_val = typename Ktraits::BlockReduceT(smem_reduce).Sum(dA_dBC_val);
431
+ dA_val = complex_t(dA_dBC_val.x, dA_dBC_val.y);
432
+ dBC_val = complex_t(dA_dBC_val.z, dA_dBC_val.w);
433
+ if (threadIdx.x == 0) {
434
+ smem_dbc[state_idx] = chunk == params.n_chunks - 1 ? dBC_val : dBC_val + smem_dbc[state_idx];
435
+ }
436
+ } else {
437
+ dA_val = typename Ktraits::BlockReduceComplexT(smem_reduce_complex).Sum(dA_val);
438
+ }
439
+ if (threadIdx.x == 0) {
440
+ smem_da[state_idx] = chunk == params.n_chunks - 1 ? dA_val : dA_val + smem_da[state_idx];
441
+ }
442
+ }
443
+ }
444
+
445
+ if constexpr (kDeltaSoftplus) {
446
+ __syncthreads();
447
+ input_t delta_vals_load[kNItems];
448
+ load_input<Ktraits>(delta, delta_vals_load, smem_load, params.seqlen - chunk * kChunkSize);
449
+ delta -= kChunkSize;
450
+ #pragma unroll
451
+ for (int i = 0; i < kNItems; ++i) {
452
+ float delta_val = float(delta_vals_load[i]) + delta_bias;
453
+ float delta_val_neg_exp = expf(-delta_val);
454
+ ddelta_vals[i] = delta_val <= 20.f
455
+ ? ddelta_vals[i] / (1.f + delta_val_neg_exp)
456
+ : ddelta_vals[i];
457
+ }
458
+ }
459
+ for (int i = 0; i < kNItems; ++i) { ddelta_bias_val += ddelta_vals[i]; }
460
+
461
+ input_t *du = reinterpret_cast<input_t *>(params.du_ptr) + batch_id * params.du_batch_stride
462
+ + dim_id * params.du_d_stride + chunk * kChunkSize;
463
+ input_t *ddelta = reinterpret_cast<input_t *>(params.ddelta_ptr) + batch_id * params.ddelta_batch_stride
464
+ + dim_id * params.ddelta_d_stride + chunk * kChunkSize;
465
+ __syncthreads();
466
+ store_output<Ktraits>(du, du_vals, smem_store, params.seqlen - chunk * kChunkSize);
467
+ __syncthreads();
468
+ store_output<Ktraits>(ddelta, ddelta_vals, smem_store, params.seqlen - chunk * kChunkSize);
469
+
470
+ Bvar -= kChunkSize * (!kIsComplex ? 1 : 2);
471
+ Cvar -= kChunkSize * (!kIsComplex ? 1 : 2);
472
+ }
473
+ if (params.dD_ptr != nullptr) {
474
+ dD_val = typename Ktraits::BlockReduceFloatT(smem_reduce_float).Sum(dD_val);
475
+ if (threadIdx.x == 0) { gpuAtomicAdd(dD, dD_val); }
476
+ }
477
+ if (params.ddelta_bias_ptr != nullptr) {
478
+ __syncthreads();
479
+ ddelta_bias_val = typename Ktraits::BlockReduceFloatT(smem_reduce_float).Sum(ddelta_bias_val);
480
+ if (threadIdx.x == 0) { gpuAtomicAdd(ddelta_bias, ddelta_bias_val); }
481
+ }
482
+ for (int state_idx = threadIdx.x; state_idx < params.dstate; state_idx += blockDim.x) {
483
+ gpuAtomicAdd(&(dA[state_idx * params.dA_dstate_stride]), smem_da[state_idx]);
484
+ weight_t dBC_val;
485
+ if (!kIsVariableB || !kIsVariableC) { dBC_val = smem_dbc[state_idx]; }
486
+ if constexpr (!kIsVariableB) {
487
+ gpuAtomicAdd(&(dB[state_idx * params.dB_dstate_stride]),
488
+ !kIsVariableC ? dBC_val * conj(C[state_idx * params.C_dstate_stride]) : dBC_val);
489
+ }
490
+ if constexpr (!kIsVariableC) {
491
+ gpuAtomicAdd(&(dC[state_idx * params.dC_dstate_stride]),
492
+ !kIsVariableB ? dBC_val * conj(B[state_idx * params.B_dstate_stride]) : dBC_val);
493
+ }
494
+ }
495
+ }
496
+
497
+ template<int kNThreads, int kNItems, typename input_t, typename weight_t>
498
+ void selective_scan_bwd_launch(SSMParamsBwd &params, cudaStream_t stream) {
499
+ BOOL_SWITCH(params.seqlen % (kNThreads * kNItems) == 0, kIsEvenLen, [&] {
500
+ BOOL_SWITCH(params.is_variable_B, kIsVariableB, [&] {
501
+ BOOL_SWITCH(params.is_variable_C, kIsVariableC, [&] {
502
+ BOOL_SWITCH(params.delta_softplus, kDeltaSoftplus, [&] {
503
+ BOOL_SWITCH(params.z_ptr != nullptr , kHasZ, [&] {
504
+ using Ktraits = Selective_Scan_bwd_kernel_traits<kNThreads, kNItems, kIsEvenLen, kIsVariableB, kIsVariableC, kDeltaSoftplus, kHasZ, input_t, weight_t>;
505
+ // using Ktraits = Selective_Scan_bwd_kernel_traits<kNThreads, kNItems, true, kIsVariableB, kIsVariableC, kDeltaSoftplus, kHasZ, input_t, weight_t>;
506
+ // TODO: check this
507
+ constexpr int kSmemSize = Ktraits::kSmemSize + MAX_DSTATE * sizeof(typename Ktraits::scan_t) + (kNThreads + 4 * MAX_DSTATE) * sizeof(typename Ktraits::weight_t);
508
+
509
+ dim3 grid(params.batch, params.dim);
510
+
511
+ auto kernel = &selective_scan_bwd_kernel<Ktraits>;
512
+
513
+ if (kSmemSize >= 48 * 1024) {
514
+
515
+ #ifndef USE_ROCM
516
+ C10_CUDA_CHECK(cudaFuncSetAttribute(
517
+ kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, kSmemSize));
518
+ #else
519
+ C10_CUDA_CHECK(cudaFuncSetAttribute(
520
+ (void *) kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, kSmemSize));
521
+ std::cerr << "Warning (selective_scan_bwd_kernel): attempting to set maxDynamicSharedMemorySize on an AMD GPU which is currently a non-op (in ROCm versions <= 6.1). This might lead to undefined behavior. \n" << std::endl;
522
+ #endif
523
+
524
+ }
525
+
526
+ kernel<<<grid, Ktraits::kNThreads, kSmemSize, stream>>>(params);
527
+ C10_CUDA_KERNEL_LAUNCH_CHECK();
528
+ });
529
+ });
530
+ });
531
+ });
532
+ });
533
+ }
534
+
535
+ template<typename input_t, typename weight_t>
536
+ void selective_scan_bwd_cuda(SSMParamsBwd &params, cudaStream_t stream) {
537
+
538
+ #ifndef USE_ROCM
539
+ if (params.seqlen <= 128) {
540
+ selective_scan_bwd_launch<32, 4, input_t, weight_t>(params, stream);
541
+ } else if (params.seqlen <= 256) {
542
+ selective_scan_bwd_launch<32, 8, input_t, weight_t>(params, stream);
543
+ } else if (params.seqlen <= 512) {
544
+ selective_scan_bwd_launch<32, 16, input_t, weight_t>(params, stream);
545
+ } else if (params.seqlen <= 1024) {
546
+ selective_scan_bwd_launch<64, 16, input_t, weight_t>(params, stream);
547
+ } else {
548
+ selective_scan_bwd_launch<128, 16, input_t, weight_t>(params, stream);
549
+ }
550
+ #else
551
+ if (params.seqlen <= 256) {
552
+ selective_scan_bwd_launch<64, 4, input_t, weight_t>(params, stream);
553
+ } else if (params.seqlen <= 512) {
554
+ selective_scan_bwd_launch<64, 8, input_t, weight_t>(params, stream);
555
+ } else if (params.seqlen <= 1024) {
556
+ selective_scan_bwd_launch<64, 16, input_t, weight_t>(params, stream);
557
+ } else {
558
+ selective_scan_bwd_launch<128, 16, input_t, weight_t>(params, stream);
559
+ }
560
+ #endif
561
+ }
vendor/mamba-og/csrc/selective_scan/selective_scan_common.h ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ #pragma once
6
+
7
+ #ifndef USE_ROCM
8
+ #include <cuda_bf16.h>
9
+ #else
10
+ #include <hip/hip_bf16.h>
11
+ #endif
12
+ #include <cuda_fp16.h>
13
+ #include <c10/util/complex.h> // For scalar_value_type
14
+
15
+
16
+ #ifndef USE_ROCM
17
+
18
+ constexpr size_t custom_max(std::initializer_list<size_t> ilist)
19
+ {
20
+ return std::max(ilist);
21
+ }
22
+
23
+ template<typename T>
24
+ constexpr T constexpr_min(T a, T b) {
25
+ return std::min(a, b);
26
+ }
27
+
28
+ #else
29
+ constexpr size_t custom_max(std::initializer_list<size_t> ilist)
30
+ {
31
+ return *std::max_element(ilist.begin(), ilist.end());
32
+ }
33
+
34
+ template<typename T>
35
+ constexpr T constexpr_min(T a, T b) {
36
+ return a < b ? a : b;
37
+ }
38
+ #endif
39
+
40
+
41
+ #define MAX_DSTATE 256
42
+
43
+ using complex_t = c10::complex<float>;
44
+
45
+ inline __device__ float2 operator+(const float2 & a, const float2 & b){
46
+ return {a.x + b.x, a.y + b.y};
47
+ }
48
+
49
+ inline __device__ float3 operator+(const float3 &a, const float3 &b) {
50
+ return {a.x + b.x, a.y + b.y, a.z + b.z};
51
+ }
52
+
53
+ inline __device__ float4 operator+(const float4 & a, const float4 & b){
54
+ return {a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w};
55
+ }
56
+
57
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
58
+
59
+ template<int BYTES> struct BytesToType {};
60
+
61
+ template<> struct BytesToType<16> {
62
+ using Type = uint4;
63
+ static_assert(sizeof(Type) == 16);
64
+ };
65
+
66
+ template<> struct BytesToType<8> {
67
+ using Type = uint64_t;
68
+ static_assert(sizeof(Type) == 8);
69
+ };
70
+
71
+ template<> struct BytesToType<4> {
72
+ using Type = uint32_t;
73
+ static_assert(sizeof(Type) == 4);
74
+ };
75
+
76
+ template<> struct BytesToType<2> {
77
+ using Type = uint16_t;
78
+ static_assert(sizeof(Type) == 2);
79
+ };
80
+
81
+ template<> struct BytesToType<1> {
82
+ using Type = uint8_t;
83
+ static_assert(sizeof(Type) == 1);
84
+ };
85
+
86
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
87
+
88
+ template<typename scalar_t, int N>
89
+ struct Converter{
90
+ static inline __device__ void to_float(const scalar_t (&src)[N], float (&dst)[N]) {
91
+ #pragma unroll
92
+ for (int i = 0; i < N; ++i) { dst[i] = src[i]; }
93
+ }
94
+ };
95
+
96
+ template<int N>
97
+ struct Converter<at::Half, N>{
98
+ static inline __device__ void to_float(const at::Half (&src)[N], float (&dst)[N]) {
99
+ static_assert(N % 2 == 0);
100
+ auto &src2 = reinterpret_cast<const half2 (&)[N / 2]>(src);
101
+ auto &dst2 = reinterpret_cast<float2 (&)[N / 2]>(dst);
102
+ #pragma unroll
103
+ for (int i = 0; i < N / 2; ++i) { dst2[i] = __half22float2(src2[i]); }
104
+ }
105
+ };
106
+
107
+ #if __CUDA_ARCH__ >= 800
108
+ template<int N>
109
+ struct Converter<at::BFloat16, N>{
110
+ static inline __device__ void to_float(const at::BFloat16 (&src)[N], float (&dst)[N]) {
111
+ static_assert(N % 2 == 0);
112
+ auto &src2 = reinterpret_cast<const nv_bfloat162 (&)[N / 2]>(src);
113
+ auto &dst2 = reinterpret_cast<float2 (&)[N / 2]>(dst);
114
+ #pragma unroll
115
+ for (int i = 0; i < N / 2; ++i) { dst2[i] = __bfloat1622float2(src2[i]); }
116
+ }
117
+ };
118
+ #endif
119
+
120
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
121
+
122
+ // From https://stackoverflow.com/questions/9860711/cucomplex-h-and-exp
123
+ // and https://forums.developer.nvidia.com/t/complex-number-exponential-function/24696
124
+ __device__ __forceinline__ complex_t cexp2f(complex_t z) {
125
+ float t = exp2f(z.real_);
126
+ float c, s;
127
+ sincosf(z.imag_, &s, &c);
128
+ return complex_t(c * t, s * t);
129
+ }
130
+
131
+ __device__ __forceinline__ complex_t cexpf(complex_t z) {
132
+ float t = expf(z.real_);
133
+ float c, s;
134
+ sincosf(z.imag_, &s, &c);
135
+ return complex_t(c * t, s * t);
136
+ }
137
+
138
+ template<typename scalar_t> struct SSMScanOp;
139
+
140
+ template<>
141
+ struct SSMScanOp<float> {
142
+ __device__ __forceinline__ float2 operator()(const float2 &ab0, const float2 &ab1) const {
143
+ return make_float2(ab1.x * ab0.x, ab1.x * ab0.y + ab1.y);
144
+ }
145
+ };
146
+
147
+ template<>
148
+ struct SSMScanOp<complex_t> {
149
+ __device__ __forceinline__ float4 operator()(const float4 &ab0, const float4 &ab1) const {
150
+ complex_t a0 = complex_t(ab0.x, ab0.y);
151
+ complex_t b0 = complex_t(ab0.z, ab0.w);
152
+ complex_t a1 = complex_t(ab1.x, ab1.y);
153
+ complex_t b1 = complex_t(ab1.z, ab1.w);
154
+ complex_t out_a = a1 * a0;
155
+ complex_t out_b = a1 * b0 + b1;
156
+ return make_float4(out_a.real_, out_a.imag_, out_b.real_, out_b.imag_);
157
+ }
158
+ };
159
+
160
+ // A stateful callback functor that maintains a running prefix to be applied
161
+ // during consecutive scan operations.
162
+ template <typename scalar_t> struct SSMScanPrefixCallbackOp {
163
+ using scan_t = std::conditional_t<std::is_same_v<scalar_t, float>, float2, float4>;
164
+ scan_t running_prefix;
165
+ // Constructor
166
+ __device__ SSMScanPrefixCallbackOp(scan_t running_prefix_) : running_prefix(running_prefix_) {}
167
+ // Callback operator to be entered by the first warp of threads in the block.
168
+ // Thread-0 is responsible for returning a value for seeding the block-wide scan.
169
+ __device__ scan_t operator()(scan_t block_aggregate) {
170
+ scan_t old_prefix = running_prefix;
171
+ running_prefix = SSMScanOp<scalar_t>()(running_prefix, block_aggregate);
172
+ return old_prefix;
173
+ }
174
+ };
175
+
176
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
177
+
178
+ template<typename Ktraits>
179
+ inline __device__ void load_input(typename Ktraits::input_t *u,
180
+ typename Ktraits::input_t (&u_vals)[Ktraits::kNItems],
181
+ typename Ktraits::BlockLoadT::TempStorage &smem_load,
182
+ int seqlen) {
183
+ if constexpr (Ktraits::kIsEvenLen) {
184
+ auto& smem_load_vec = reinterpret_cast<typename Ktraits::BlockLoadVecT::TempStorage&>(smem_load);
185
+ using vec_t = typename Ktraits::vec_t;
186
+ typename Ktraits::BlockLoadVecT(smem_load_vec).Load(
187
+ reinterpret_cast<vec_t*>(u),
188
+ reinterpret_cast<vec_t(&)[Ktraits::kNLoads]>(u_vals)
189
+ #ifdef USE_ROCM
190
+ , Ktraits::kNThreads * Ktraits::kNLoads
191
+ #endif
192
+
193
+ );
194
+ } else {
195
+ typename Ktraits::BlockLoadT(smem_load).Load(u, u_vals, seqlen, 0.f);
196
+ }
197
+ }
198
+
199
+ template<typename Ktraits>
200
+ inline __device__ void load_weight(typename Ktraits::input_t *Bvar,
201
+ typename Ktraits::weight_t (&B_vals)[Ktraits::kNItems],
202
+ typename Ktraits::BlockLoadWeightT::TempStorage &smem_load_weight,
203
+ int seqlen) {
204
+ constexpr int kNItems = Ktraits::kNItems;
205
+ if constexpr (!Ktraits::kIsComplex) {
206
+ typename Ktraits::input_t B_vals_load[kNItems];
207
+ if constexpr (Ktraits::kIsEvenLen) {
208
+ auto& smem_load_weight_vec = reinterpret_cast<typename Ktraits::BlockLoadWeightVecT::TempStorage&>(smem_load_weight);
209
+ using vec_t = typename Ktraits::vec_t;
210
+ typename Ktraits::BlockLoadWeightVecT(smem_load_weight_vec).Load(
211
+ reinterpret_cast<vec_t*>(Bvar),
212
+ reinterpret_cast<vec_t(&)[Ktraits::kNLoads]>(B_vals_load)
213
+ );
214
+ } else {
215
+ typename Ktraits::BlockLoadWeightT(smem_load_weight).Load(Bvar, B_vals_load, seqlen, 0.f);
216
+ }
217
+ // #pragma unroll
218
+ // for (int i = 0; i < kNItems; ++i) { B_vals[i] = B_vals_load[i]; }
219
+ Converter<typename Ktraits::input_t, kNItems>::to_float(B_vals_load, B_vals);
220
+ } else {
221
+ typename Ktraits::input_t B_vals_load[kNItems * 2];
222
+ if constexpr (Ktraits::kIsEvenLen) {
223
+ auto& smem_load_weight_vec = reinterpret_cast<typename Ktraits::BlockLoadWeightVecT::TempStorage&>(smem_load_weight);
224
+ using vec_t = typename Ktraits::vec_t;
225
+ typename Ktraits::BlockLoadWeightVecT(smem_load_weight_vec).Load(
226
+ reinterpret_cast<vec_t*>(Bvar),
227
+ reinterpret_cast<vec_t(&)[Ktraits::kNLoads * 2]>(B_vals_load)
228
+ );
229
+ } else {
230
+ typename Ktraits::BlockLoadWeightT(smem_load_weight).Load(Bvar, B_vals_load, seqlen, 0.f);
231
+ }
232
+ #pragma unroll
233
+ for (int i = 0; i < kNItems; ++i) { B_vals[i] = complex_t(B_vals_load[i * 2], B_vals_load[i * 2 + 1]); }
234
+ }
235
+ }
236
+
237
+ template<typename Ktraits>
238
+ inline __device__ void store_output(typename Ktraits::input_t *out,
239
+ const float (&out_vals)[Ktraits::kNItems],
240
+ typename Ktraits::BlockStoreT::TempStorage &smem_store,
241
+ int seqlen) {
242
+ typename Ktraits::input_t write_vals[Ktraits::kNItems];
243
+ #pragma unroll
244
+ for (int i = 0; i < Ktraits::kNItems; ++i) { write_vals[i] = out_vals[i]; }
245
+ if constexpr (Ktraits::kIsEvenLen) {
246
+ auto& smem_store_vec = reinterpret_cast<typename Ktraits::BlockStoreVecT::TempStorage&>(smem_store);
247
+ using vec_t = typename Ktraits::vec_t;
248
+ typename Ktraits::BlockStoreVecT(smem_store_vec).Store(
249
+ reinterpret_cast<vec_t*>(out),
250
+ reinterpret_cast<vec_t(&)[Ktraits::kNLoads]>(write_vals)
251
+ );
252
+ } else {
253
+ typename Ktraits::BlockStoreT(smem_store).Store(out, write_vals, seqlen);
254
+ }
255
+ }
vendor/mamba-og/csrc/selective_scan/selective_scan_fwd_bf16.cu ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ // Split into multiple files to compile in paralell
6
+
7
+ #include "selective_scan_fwd_kernel.cuh"
8
+
9
+ template void selective_scan_fwd_cuda<at::BFloat16, float>(SSMParamsBase &params, cudaStream_t stream);
10
+ template void selective_scan_fwd_cuda<at::BFloat16, complex_t>(SSMParamsBase &params, cudaStream_t stream);
vendor/mamba-og/csrc/selective_scan/selective_scan_fwd_fp16.cu ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ // Split into multiple files to compile in paralell
6
+
7
+ #include "selective_scan_fwd_kernel.cuh"
8
+
9
+ template void selective_scan_fwd_cuda<at::Half, float>(SSMParamsBase &params, cudaStream_t stream);
10
+ template void selective_scan_fwd_cuda<at::Half, complex_t>(SSMParamsBase &params, cudaStream_t stream);
vendor/mamba-og/csrc/selective_scan/selective_scan_fwd_fp32.cu ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ // Split into multiple files to compile in paralell
6
+
7
+ #include "selective_scan_fwd_kernel.cuh"
8
+
9
+ template void selective_scan_fwd_cuda<float, float>(SSMParamsBase &params, cudaStream_t stream);
10
+ template void selective_scan_fwd_cuda<float, complex_t>(SSMParamsBase &params, cudaStream_t stream);
vendor/mamba-og/csrc/selective_scan/selective_scan_fwd_kernel.cuh ADDED
@@ -0,0 +1,376 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2023, Tri Dao.
3
+ ******************************************************************************/
4
+
5
+ #pragma once
6
+
7
+ #include <c10/util/BFloat16.h>
8
+ #include <c10/util/Half.h>
9
+ #include <c10/cuda/CUDAException.h> // For C10_CUDA_CHECK and C10_CUDA_KERNEL_LAUNCH_CHECK
10
+
11
+ #ifndef USE_ROCM
12
+ #include <cub/block/block_load.cuh>
13
+ #include <cub/block/block_store.cuh>
14
+ #include <cub/block/block_scan.cuh>
15
+ #else
16
+ #include <hipcub/hipcub.hpp>
17
+ namespace cub = hipcub;
18
+ #endif
19
+
20
+ #include "selective_scan.h"
21
+ #include "selective_scan_common.h"
22
+ #include "static_switch.h"
23
+
24
+ template<int kNThreads_, int kNItems_, int kNRows_, bool kIsEvenLen_,
25
+ bool kIsVariableB_, bool kIsVariableC_,
26
+ bool kHasZ_, typename input_t_, typename weight_t_>
27
+ struct Selective_Scan_fwd_kernel_traits {
28
+ static_assert(kNItems_ % 4 == 0);
29
+ using input_t = input_t_;
30
+ using weight_t = weight_t_;
31
+ static constexpr int kNThreads = kNThreads_;
32
+ // Setting MinBlocksPerMP to be 3 (instead of 2) for 128 threads improves occupancy.
33
+ static constexpr int kMinBlocks = kNThreads < 128 ? 5 : 3;
34
+ static constexpr int kNItems = kNItems_;
35
+ static constexpr int kNRows = kNRows_;
36
+ static constexpr int kNBytes = sizeof(input_t);
37
+ static_assert(kNBytes == 2 || kNBytes == 4);
38
+ static constexpr int kNElts = kNBytes == 4 ? 4 : constexpr_min(8, kNItems);
39
+ static_assert(kNItems % kNElts == 0);
40
+ static constexpr int kNLoads = kNItems / kNElts;
41
+ static constexpr bool kIsComplex = std::is_same_v<weight_t, complex_t>;
42
+ static constexpr bool kIsEvenLen = kIsEvenLen_;
43
+ static constexpr bool kIsVariableB = kIsVariableB_;
44
+ static constexpr bool kIsVariableC = kIsVariableC_;
45
+ static constexpr bool kHasZ = kHasZ_;
46
+
47
+ static constexpr bool kDirectIO = kIsEvenLen && kNLoads == 1;
48
+
49
+ using vec_t = typename BytesToType<kNBytes * kNElts>::Type;
50
+ using scan_t = std::conditional_t<!kIsComplex, float2, float4>;
51
+ using BlockLoadT = cub::BlockLoad<input_t, kNThreads, kNItems, cub::BLOCK_LOAD_WARP_TRANSPOSE>;
52
+ using BlockLoadVecT = cub::BlockLoad<vec_t, kNThreads, kNLoads,
53
+ !kDirectIO ? cub::BLOCK_LOAD_WARP_TRANSPOSE : cub::BLOCK_LOAD_DIRECT>;
54
+ using BlockLoadWeightT = cub::BlockLoad<input_t, kNThreads, !kIsComplex ? kNItems : kNItems * 2, cub::BLOCK_LOAD_WARP_TRANSPOSE>;
55
+ using BlockLoadWeightVecT = cub::BlockLoad<vec_t, kNThreads, !kIsComplex ? kNLoads : kNLoads * 2,
56
+ !kDirectIO ? cub::BLOCK_LOAD_WARP_TRANSPOSE : cub::BLOCK_LOAD_DIRECT>;
57
+ using BlockStoreT = cub::BlockStore<input_t, kNThreads, kNItems, cub::BLOCK_STORE_WARP_TRANSPOSE>;
58
+ using BlockStoreVecT = cub::BlockStore<vec_t, kNThreads, kNLoads,
59
+ !kDirectIO ? cub::BLOCK_STORE_WARP_TRANSPOSE : cub::BLOCK_STORE_DIRECT>;
60
+ // using BlockScanT = cub::BlockScan<scan_t, kNThreads, cub::BLOCK_SCAN_RAKING_MEMOIZE>;
61
+ // using BlockScanT = cub::BlockScan<scan_t, kNThreads, cub::BLOCK_SCAN_RAKING>;
62
+ using BlockScanT = cub::BlockScan<scan_t, kNThreads, cub::BLOCK_SCAN_WARP_SCANS>;
63
+ static constexpr int kSmemIOSize = custom_max({sizeof(typename BlockLoadT::TempStorage),
64
+ sizeof(typename BlockLoadVecT::TempStorage),
65
+ (int(kIsVariableB) + int(kIsVariableC)) * sizeof(typename BlockLoadWeightT::TempStorage),
66
+ (int(kIsVariableB) + int(kIsVariableC)) * sizeof(typename BlockLoadWeightVecT::TempStorage),
67
+ sizeof(typename BlockStoreT::TempStorage),
68
+ sizeof(typename BlockStoreVecT::TempStorage)});
69
+ static constexpr int kSmemSize = kSmemIOSize + sizeof(typename BlockScanT::TempStorage);
70
+ };
71
+
72
+ template<typename Ktraits>
73
+ __global__ __launch_bounds__(Ktraits::kNThreads, Ktraits::kMinBlocks)
74
+ void selective_scan_fwd_kernel(SSMParamsBase params) {
75
+ constexpr bool kIsComplex = Ktraits::kIsComplex;
76
+ constexpr bool kIsVariableB = Ktraits::kIsVariableB;
77
+ constexpr bool kIsVariableC = Ktraits::kIsVariableC;
78
+ constexpr bool kHasZ = Ktraits::kHasZ;
79
+ constexpr int kNThreads = Ktraits::kNThreads;
80
+ constexpr int kNItems = Ktraits::kNItems;
81
+ constexpr int kNRows = Ktraits::kNRows;
82
+ constexpr bool kDirectIO = Ktraits::kDirectIO;
83
+ using input_t = typename Ktraits::input_t;
84
+ using weight_t = typename Ktraits::weight_t;
85
+ using scan_t = typename Ktraits::scan_t;
86
+
87
+ // Shared memory.
88
+ extern __shared__ char smem_[];
89
+ // cast to lvalue reference of expected type
90
+ // char *smem_loadstorescan = smem_ + 2 * MAX_DSTATE * sizeof(weight_t);
91
+ // auto& smem_load = reinterpret_cast<typename BlockLoadT::TempStorage&>(smem_ + 2 * MAX_DSTATE * sizeof(weight_t));
92
+ // auto& smem_load = reinterpret_cast<typename BlockLoadT::TempStorage&>(smem_loadstorescan);
93
+ auto& smem_load = reinterpret_cast<typename Ktraits::BlockLoadT::TempStorage&>(smem_);
94
+ auto& smem_load_weight = reinterpret_cast<typename Ktraits::BlockLoadWeightT::TempStorage&>(smem_);
95
+ auto& smem_load_weight1 = *reinterpret_cast<typename Ktraits::BlockLoadWeightT::TempStorage*>(smem_ + sizeof(typename Ktraits::BlockLoadWeightT::TempStorage));
96
+ auto& smem_store = reinterpret_cast<typename Ktraits::BlockStoreT::TempStorage&>(smem_);
97
+ auto& smem_scan = *reinterpret_cast<typename Ktraits::BlockScanT::TempStorage*>(smem_ + Ktraits::kSmemIOSize);
98
+ // weight_t *smem_a = reinterpret_cast<weight_t *>(smem_ + smem_loadstorescan_size);
99
+ // weight_t *smem_bc = reinterpret_cast<weight_t *>(smem_a + MAX_DSTATE);
100
+ scan_t *smem_running_prefix = reinterpret_cast<scan_t *>(smem_ + Ktraits::kSmemSize);
101
+
102
+ const int batch_id = blockIdx.x;
103
+ const int dim_id = blockIdx.y;
104
+ const int group_id = dim_id / (params.dim_ngroups_ratio);
105
+ input_t *u = reinterpret_cast<input_t *>(params.u_ptr) + batch_id * params.u_batch_stride
106
+ + dim_id * kNRows * params.u_d_stride;
107
+ input_t *delta = reinterpret_cast<input_t *>(params.delta_ptr) + batch_id * params.delta_batch_stride
108
+ + dim_id * kNRows * params.delta_d_stride;
109
+ weight_t *A = reinterpret_cast<weight_t *>(params.A_ptr) + dim_id * kNRows * params.A_d_stride;
110
+ weight_t *B = reinterpret_cast<weight_t *>(params.B_ptr) + dim_id * kNRows * params.B_d_stride;
111
+ input_t *Bvar = reinterpret_cast<input_t *>(params.B_ptr) + batch_id * params.B_batch_stride + group_id * params.B_group_stride;
112
+ weight_t *C = reinterpret_cast<weight_t *>(params.C_ptr) + dim_id * kNRows * params.C_d_stride;
113
+ input_t *Cvar = reinterpret_cast<input_t *>(params.C_ptr) + batch_id * params.C_batch_stride + group_id * params.C_group_stride;
114
+ scan_t *x = reinterpret_cast<scan_t *>(params.x_ptr) + (batch_id * params.dim + dim_id * kNRows) * params.n_chunks * params.dstate;
115
+
116
+ float D_val[kNRows] = {0};
117
+ if (params.D_ptr != nullptr) {
118
+ #pragma unroll
119
+ for (int r = 0; r < kNRows; ++r) {
120
+ D_val[r] = reinterpret_cast<float *>(params.D_ptr)[dim_id * kNRows + r];
121
+ }
122
+ }
123
+ float delta_bias[kNRows] = {0};
124
+ if (params.delta_bias_ptr != nullptr) {
125
+ #pragma unroll
126
+ for (int r = 0; r < kNRows; ++r) {
127
+ delta_bias[r] = reinterpret_cast<float *>(params.delta_bias_ptr)[dim_id * kNRows + r];
128
+ }
129
+ }
130
+
131
+ // for (int state_idx = threadIdx.x; state_idx < params.dstate; state_idx += blockDim.x) {
132
+ // smem_a[state_idx] = A[state_idx * params.A_dstate_stride];
133
+ // smem_bc[state_idx] = B[state_idx * params.B_dstate_stride] * C[state_idx * params.C_dstate_stride];
134
+ // }
135
+
136
+ constexpr int kChunkSize = kNThreads * kNItems;
137
+ for (int chunk = 0; chunk < params.n_chunks; ++chunk) {
138
+ input_t u_vals[kNRows][kNItems], delta_vals_load[kNRows][kNItems];
139
+ __syncthreads();
140
+ #pragma unroll
141
+ for (int r = 0; r < kNRows; ++r) {
142
+ if constexpr (!kDirectIO) {
143
+ if (r > 0) { __syncthreads(); }
144
+ }
145
+ load_input<Ktraits>(u + r * params.u_d_stride, u_vals[r], smem_load, params.seqlen - chunk * kChunkSize);
146
+ if constexpr (!kDirectIO) { __syncthreads(); }
147
+ load_input<Ktraits>(delta + r * params.delta_d_stride, delta_vals_load[r], smem_load, params.seqlen - chunk * kChunkSize);
148
+ }
149
+ u += kChunkSize;
150
+ delta += kChunkSize;
151
+
152
+ float delta_vals[kNRows][kNItems], delta_u_vals[kNRows][kNItems], out_vals[kNRows][kNItems];
153
+ #pragma unroll
154
+ for (int r = 0; r < kNRows; ++r) {
155
+ #pragma unroll
156
+ for (int i = 0; i < kNItems; ++i) {
157
+ float u_val = float(u_vals[r][i]);
158
+ delta_vals[r][i] = float(delta_vals_load[r][i]) + delta_bias[r];
159
+ if (params.delta_softplus) {
160
+ delta_vals[r][i] = delta_vals[r][i] <= 20.f ? log1pf(expf(delta_vals[r][i])) : delta_vals[r][i];
161
+ }
162
+ delta_u_vals[r][i] = delta_vals[r][i] * u_val;
163
+ out_vals[r][i] = D_val[r] * u_val;
164
+ }
165
+ }
166
+
167
+ __syncthreads();
168
+ for (int state_idx = 0; state_idx < params.dstate; ++state_idx) {
169
+ weight_t A_val[kNRows];
170
+ #pragma unroll
171
+ for (int r = 0; r < kNRows; ++r) {
172
+ A_val[r] = A[state_idx * params.A_dstate_stride + r * params.A_d_stride];
173
+ // Multiply the real part of A with LOG2E so we can use exp2f instead of expf.
174
+ constexpr float kLog2e = M_LOG2E;
175
+ if constexpr (!kIsComplex) {
176
+ A_val[r] *= kLog2e;
177
+ } else {
178
+ A_val[r].real_ *= kLog2e;
179
+ }
180
+ }
181
+ // This variable holds B * C if both B and C are constant across seqlen. If only B varies
182
+ // across seqlen, this holds C. If only C varies across seqlen, this holds B.
183
+ // If both B and C vary, this is unused.
184
+ weight_t BC_val[kNRows];
185
+ weight_t B_vals[kNItems], C_vals[kNItems];
186
+ if constexpr (kIsVariableB) {
187
+ load_weight<Ktraits>(Bvar + state_idx * params.B_dstate_stride, B_vals,
188
+ smem_load_weight, (params.seqlen - chunk * kChunkSize) * (!kIsComplex ? 1 : 2));
189
+ if constexpr (!kIsVariableC) {
190
+ #pragma unroll
191
+ for (int r = 0; r < kNRows; ++r) {
192
+ BC_val[r] = C[state_idx * params.C_dstate_stride + r * params.C_d_stride];
193
+ }
194
+ }
195
+ }
196
+ if constexpr (kIsVariableC) {
197
+ auto &smem_load_weight_C = !kIsVariableB ? smem_load_weight : smem_load_weight1;
198
+ load_weight<Ktraits>(Cvar + state_idx * params.C_dstate_stride, C_vals,
199
+ smem_load_weight_C, (params.seqlen - chunk * kChunkSize) * (!kIsComplex ? 1 : 2));
200
+ if constexpr (!kIsVariableB) {
201
+ #pragma unroll
202
+ for (int r = 0; r < kNRows; ++r) {
203
+ BC_val[r] = B[state_idx * params.B_dstate_stride + r * params.B_d_stride];
204
+ }
205
+ }
206
+ }
207
+ if constexpr (!kIsVariableB && !kIsVariableC) {
208
+ #pragma unroll
209
+ for (int r = 0; r < kNRows; ++r) {
210
+ BC_val[r] = B[state_idx * params.B_dstate_stride + r * params.B_d_stride] * C[state_idx * params.C_dstate_stride + r * params.C_d_stride];
211
+ }
212
+ }
213
+
214
+ #pragma unroll
215
+ for (int r = 0; r < kNRows; ++r) {
216
+ if (r > 0) { __syncthreads(); } // Scan could be using the same smem
217
+ scan_t thread_data[kNItems];
218
+ #pragma unroll
219
+ for (int i = 0; i < kNItems; ++i) {
220
+ if constexpr (!kIsComplex) {
221
+ thread_data[i] = make_float2(exp2f(delta_vals[r][i] * A_val[r]),
222
+ !kIsVariableB ? delta_u_vals[r][i] : B_vals[i] * delta_u_vals[r][i]);
223
+ if constexpr (!Ktraits::kIsEvenLen) { // So that the last state is correct
224
+ if (threadIdx.x * kNItems + i >= params.seqlen - chunk * kChunkSize) {
225
+ thread_data[i] = make_float2(1.f, 0.f);
226
+ }
227
+ }
228
+ } else {
229
+ // Pytorch's implementation of complex exp (which calls thrust) is very slow
230
+ complex_t delta_a_exp = cexp2f(delta_vals[r][i] * A_val[r]);
231
+ weight_t B_delta_u_val = !kIsVariableB ? delta_u_vals[r][i] : B_vals[i] * delta_u_vals[r][i];
232
+ thread_data[i] = make_float4(delta_a_exp.real_, delta_a_exp.imag_, B_delta_u_val.real_, B_delta_u_val.imag_);
233
+ if constexpr (!Ktraits::kIsEvenLen) { // So that the last state is correct
234
+ if (threadIdx.x * kNItems + i >= params.seqlen - chunk * kChunkSize) {
235
+ thread_data[i] = make_float4(1.f, 0.f, 0.f, 0.f);
236
+ }
237
+ }
238
+ }
239
+ }
240
+ // Initialize running total
241
+ scan_t running_prefix;
242
+ if constexpr (!kIsComplex) {
243
+ // If we use WARP_SCAN then all lane 0 of all warps (not just thread 0) needs to read
244
+ running_prefix = chunk > 0 && threadIdx.x % 32 == 0 ? smem_running_prefix[state_idx + r * MAX_DSTATE] : make_float2(1.f, 0.f);
245
+ // running_prefix = chunk > 0 && threadIdx.x == 0 ? smem_running_prefix[state_idx] : make_float2(1.f, 0.f);
246
+ } else {
247
+ running_prefix = chunk > 0 && threadIdx.x % 32 == 0 ? smem_running_prefix[state_idx + r * MAX_DSTATE] : make_float4(1.f, 0.f, 0.f, 0.f);
248
+ // running_prefix = chunk > 0 && threadIdx.x == 0 ? smem_running_prefix[state_idx] : make_float4(1.f, 0.f, 0.f, 0.f);
249
+ }
250
+ SSMScanPrefixCallbackOp<weight_t> prefix_op(running_prefix);
251
+ typename Ktraits::BlockScanT(smem_scan).InclusiveScan(
252
+ thread_data, thread_data, SSMScanOp<weight_t>(), prefix_op
253
+ );
254
+ // There's a syncthreads in the scan op, so we don't need to sync here.
255
+ // Unless there's only 1 warp, but then it's the same thread (0) reading and writing.
256
+ if (threadIdx.x == 0) {
257
+ smem_running_prefix[state_idx + r * MAX_DSTATE] = prefix_op.running_prefix;
258
+ x[(r * params.n_chunks + chunk) * params.dstate + state_idx] = prefix_op.running_prefix;
259
+ }
260
+ #pragma unroll
261
+ for (int i = 0; i < kNItems; ++i) {
262
+ const weight_t C_val = !kIsVariableC
263
+ ? BC_val[r]
264
+ : (!kIsVariableB ? BC_val[r] * C_vals[i] : C_vals[i]);
265
+ if constexpr (!kIsComplex) {
266
+ out_vals[r][i] += thread_data[i].y * C_val;
267
+ } else {
268
+ out_vals[r][i] += (complex_t(thread_data[i].z, thread_data[i].w) * C_val).real_ * 2;
269
+ }
270
+ }
271
+ }
272
+ }
273
+
274
+ input_t *out = reinterpret_cast<input_t *>(params.out_ptr) + batch_id * params.out_batch_stride
275
+ + dim_id * kNRows * params.out_d_stride + chunk * kChunkSize;
276
+ __syncthreads();
277
+ #pragma unroll
278
+ for (int r = 0; r < kNRows; ++r) {
279
+ if constexpr (!kDirectIO) {
280
+ if (r > 0) { __syncthreads(); }
281
+ }
282
+ store_output<Ktraits>(out + r * params.out_d_stride, out_vals[r], smem_store, params.seqlen - chunk * kChunkSize);
283
+ }
284
+
285
+ if constexpr (kHasZ) {
286
+ input_t *z = reinterpret_cast<input_t *>(params.z_ptr) + batch_id * params.z_batch_stride
287
+ + dim_id * kNRows * params.z_d_stride + chunk * kChunkSize;
288
+ input_t *out_z = reinterpret_cast<input_t *>(params.out_z_ptr) + batch_id * params.out_z_batch_stride
289
+ + dim_id * kNRows * params.out_z_d_stride + chunk * kChunkSize;
290
+ #pragma unroll
291
+ for (int r = 0; r < kNRows; ++r) {
292
+ input_t z_vals[kNItems];
293
+ __syncthreads();
294
+ load_input<Ktraits>(z + r * params.z_d_stride, z_vals, smem_load, params.seqlen - chunk * kChunkSize);
295
+ #pragma unroll
296
+ for (int i = 0; i < kNItems; ++i) {
297
+ float z_val = z_vals[i];
298
+ out_vals[r][i] *= z_val / (1 + expf(-z_val));
299
+ }
300
+ __syncthreads();
301
+ store_output<Ktraits>(out_z + r * params.out_z_d_stride, out_vals[r], smem_store, params.seqlen - chunk * kChunkSize);
302
+ }
303
+ }
304
+
305
+ Bvar += kChunkSize * (!kIsComplex ? 1 : 2);
306
+ Cvar += kChunkSize * (!kIsComplex ? 1 : 2);
307
+ }
308
+ }
309
+
310
+ template<int kNThreads, int kNItems, typename input_t, typename weight_t>
311
+ void selective_scan_fwd_launch(SSMParamsBase &params, cudaStream_t stream) {
312
+ // Only kNRows == 1 is tested for now, which ofc doesn't differ from previously when we had each block
313
+ // processing 1 row.
314
+ constexpr int kNRows = 1;
315
+ BOOL_SWITCH(params.seqlen % (kNThreads * kNItems) == 0, kIsEvenLen, [&] {
316
+ BOOL_SWITCH(params.is_variable_B, kIsVariableB, [&] {
317
+ BOOL_SWITCH(params.is_variable_C, kIsVariableC, [&] {
318
+ BOOL_SWITCH(params.z_ptr != nullptr , kHasZ, [&] {
319
+ using Ktraits = Selective_Scan_fwd_kernel_traits<kNThreads, kNItems, kNRows, kIsEvenLen, kIsVariableB, kIsVariableC, kHasZ, input_t, weight_t>;
320
+
321
+ constexpr int kSmemSize = Ktraits::kSmemSize + kNRows * MAX_DSTATE * sizeof(typename Ktraits::scan_t);
322
+ dim3 grid(params.batch, params.dim / kNRows);
323
+
324
+ // Had to change this substantially since potentially the hip
325
+ // interface for setting kernel launch attributes is slightly different from
326
+ // cuda's. In particualar, it seems to expect a plain const void * pointer.
327
+
328
+ auto kernel = &selective_scan_fwd_kernel<Ktraits>;
329
+
330
+
331
+ if (kSmemSize >= 48 * 1024) {
332
+ #ifndef USE_ROCM
333
+ C10_CUDA_CHECK(cudaFuncSetAttribute(
334
+ kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, kSmemSize));
335
+ #else
336
+ C10_CUDA_CHECK(cudaFuncSetAttribute(
337
+ (void *) kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, kSmemSize));
338
+ std::cerr << "Warning (selective_scan_fwd_kernel): attempting to set maxDynamicSharedMemorySize on an AMD GPU which is currently a non-op (in ROCm versions <= 6.1). This might lead to undefined behavior. \n" << std::endl;
339
+ #endif
340
+ }
341
+
342
+ kernel<<<grid, Ktraits::kNThreads, kSmemSize, stream>>>(params);
343
+ C10_CUDA_KERNEL_LAUNCH_CHECK();
344
+ });
345
+ });
346
+ });
347
+ });
348
+ }
349
+
350
+ template<typename input_t, typename weight_t>
351
+ void selective_scan_fwd_cuda(SSMParamsBase &params, cudaStream_t stream) {
352
+
353
+ #ifndef USE_ROCM
354
+ if (params.seqlen <= 128) {
355
+ selective_scan_fwd_launch<32, 4, input_t, weight_t>(params, stream);
356
+ } else if (params.seqlen <= 256) {
357
+ selective_scan_fwd_launch<32, 8, input_t, weight_t>(params, stream);
358
+ } else if (params.seqlen <= 512) {
359
+ selective_scan_fwd_launch<32, 16, input_t, weight_t>(params, stream);
360
+ } else if (params.seqlen <= 1024) {
361
+ selective_scan_fwd_launch<64, 16, input_t, weight_t>(params, stream);
362
+ } else {
363
+ selective_scan_fwd_launch<128, 16, input_t, weight_t>(params, stream);
364
+ }
365
+ #else
366
+ if (params.seqlen <= 256) {
367
+ selective_scan_fwd_launch<64, 4, input_t, weight_t>(params, stream);
368
+ } else if (params.seqlen <= 512) {
369
+ selective_scan_fwd_launch<64, 8, input_t, weight_t>(params, stream);
370
+ } else if (params.seqlen <= 1024) {
371
+ selective_scan_fwd_launch<64, 16, input_t, weight_t>(params, stream);
372
+ } else {
373
+ selective_scan_fwd_launch<128, 16, input_t, weight_t>(params, stream);
374
+ }
375
+ #endif
376
+ }
vendor/mamba-og/csrc/selective_scan/static_switch.h ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Inspired by https://github.com/NVIDIA/DALI/blob/main/include/dali/core/static_switch.h
2
+ // and https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Dispatch.h
3
+
4
+ #pragma once
5
+
6
+ /// @param COND - a boolean expression to switch by
7
+ /// @param CONST_NAME - a name given for the constexpr bool variable.
8
+ /// @param ... - code to execute for true and false
9
+ ///
10
+ /// Usage:
11
+ /// ```
12
+ /// BOOL_SWITCH(flag, BoolConst, [&] {
13
+ /// some_function<BoolConst>(...);
14
+ /// });
15
+ /// ```
16
+ #define BOOL_SWITCH(COND, CONST_NAME, ...) \
17
+ [&] { \
18
+ if (COND) { \
19
+ constexpr bool CONST_NAME = true; \
20
+ return __VA_ARGS__(); \
21
+ } else { \
22
+ constexpr bool CONST_NAME = false; \
23
+ return __VA_ARGS__(); \
24
+ } \
25
+ }()
vendor/mamba-og/csrc/selective_scan/uninitialized_copy.cuh ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************
2
+ * Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.
3
+ *
4
+ * Redistribution and use in source and binary forms, with or without
5
+ * modification, are permitted provided that the following conditions are met:
6
+ * * Redistributions of source code must retain the above copyright
7
+ * notice, this list of conditions and the following disclaimer.
8
+ * * Redistributions in binary form must reproduce the above copyright
9
+ * notice, this list of conditions and the following disclaimer in the
10
+ * documentation and/or other materials provided with the distribution.
11
+ * * Neither the name of the NVIDIA CORPORATION nor the
12
+ * names of its contributors may be used to endorse or promote products
13
+ * derived from this software without specific prior written permission.
14
+ *
15
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ * ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
19
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+ *
26
+ ******************************************************************************/
27
+
28
+ #pragma once
29
+
30
+ #ifndef USE_ROCM
31
+ #include <cub/config.cuh>
32
+
33
+ #include <cuda/std/type_traits>
34
+ #else
35
+ #include <hipcub/hipcub.hpp>
36
+ // Map ::cuda::std to the standard std namespace
37
+ namespace cuda {
38
+ namespace std = ::std;
39
+ }
40
+ #endif
41
+
42
+
43
+ namespace detail
44
+ {
45
+
46
+ #if defined(_NVHPC_CUDA)
47
+ template <typename T, typename U>
48
+ __host__ __device__ void uninitialized_copy(T *ptr, U &&val)
49
+ {
50
+ // NVBug 3384810
51
+ new (ptr) T(::cuda::std::forward<U>(val));
52
+ }
53
+ #else
54
+ template <typename T,
55
+ typename U,
56
+ typename ::cuda::std::enable_if<
57
+ ::cuda::std::is_trivially_copyable<T>::value,
58
+ int
59
+ >::type = 0>
60
+ __host__ __device__ void uninitialized_copy(T *ptr, U &&val)
61
+ {
62
+ *ptr = ::cuda::std::forward<U>(val);
63
+ }
64
+
65
+ template <typename T,
66
+ typename U,
67
+ typename ::cuda::std::enable_if<
68
+ !::cuda::std::is_trivially_copyable<T>::value,
69
+ int
70
+ >::type = 0>
71
+ __host__ __device__ void uninitialized_copy(T *ptr, U &&val)
72
+ {
73
+ new (ptr) T(::cuda::std::forward<U>(val));
74
+ }
75
+ #endif
76
+
77
+ } // namespace detail
vendor/mamba-og/evals/lm_harness_eval.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ import transformers
4
+ from transformers import AutoTokenizer
5
+
6
+ from mamba_ssm.models.mixer_seq_simple import MambaLMHeadModel
7
+
8
+ from lm_eval.api.model import LM
9
+ from lm_eval.models.huggingface import HFLM
10
+ from lm_eval.api.registry import register_model
11
+ from lm_eval.__main__ import cli_evaluate
12
+
13
+
14
+ @register_model("mamba")
15
+ class MambaEvalWrapper(HFLM):
16
+
17
+ AUTO_MODEL_CLASS = transformers.AutoModelForCausalLM
18
+
19
+ def __init__(self, pretrained="state-spaces/mamba-2.8b", max_length=2048, batch_size=None, device="cuda",
20
+ dtype=torch.float16):
21
+ LM.__init__(self)
22
+ self._model = MambaLMHeadModel.from_pretrained(pretrained, device=device, dtype=dtype)
23
+ self.tokenizer = AutoTokenizer.from_pretrained("EleutherAI/gpt-neox-20b")
24
+ self.tokenizer.pad_token_id = self.tokenizer.eos_token_id
25
+ self.vocab_size = self.tokenizer.vocab_size
26
+ self._batch_size = int(batch_size) if batch_size is not None else 64
27
+ self._max_length = max_length
28
+ self._device = torch.device(device)
29
+
30
+ @property
31
+ def batch_size(self):
32
+ return self._batch_size
33
+
34
+ def _model_generate(self, context, max_length, stop, **generation_kwargs):
35
+ raise NotImplementedError()
36
+
37
+
38
+ if __name__ == "__main__":
39
+ cli_evaluate()
vendor/mamba-og/mamba_ssm/__init__.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __version__ = "2.3.2.post1"
2
+
3
+ try:
4
+ from mamba_ssm.ops.selective_scan_interface import selective_scan_fn, mamba_inner_fn
5
+ except ImportError:
6
+ selective_scan_fn = None
7
+ mamba_inner_fn = None
8
+
9
+ try:
10
+ from mamba_ssm.modules.mamba_simple import Mamba
11
+ except ImportError:
12
+ Mamba = None
13
+
14
+ try:
15
+ from mamba_ssm.modules.mamba2 import Mamba2
16
+ except ImportError:
17
+ Mamba2 = None
18
+
19
+ from mamba_ssm.modules.mamba3 import Mamba3
20
+
21
+ try:
22
+ from mamba_ssm.models.mixer_seq_simple import MambaLMHeadModel
23
+ except ImportError:
24
+ MambaLMHeadModel = None
vendor/mamba-og/mamba_ssm/distributed/__init__.py ADDED
File without changes
vendor/mamba-og/mamba_ssm/distributed/distributed_utils.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ import torch
4
+ from torch import Tensor
5
+ from torch.distributed import ProcessGroup
6
+
7
+ # `all_gather_into_tensor` and `reduce_scatter_tensor` are new placeholders for
8
+ # `_all_gather_base` and `_reduce_scatter_base`. They require the most recent
9
+ # version of PyTorch. The following 4 lines are for backward compatibility with
10
+ # older PyTorch.
11
+ if "all_gather_into_tensor" not in dir(torch.distributed):
12
+ torch.distributed.all_gather_into_tensor = torch.distributed._all_gather_base
13
+ if "reduce_scatter_tensor" not in dir(torch.distributed):
14
+ torch.distributed.reduce_scatter_tensor = torch.distributed._reduce_scatter_base
15
+
16
+
17
+ # Raw operation, does not support autograd, but does support async
18
+ def all_gather_raw(input_: Tensor, process_group: ProcessGroup, async_op: bool = False):
19
+ world_size = torch.distributed.get_world_size(process_group)
20
+ output = torch.empty(
21
+ world_size * input_.shape[0], *input_.shape[1:], dtype=input_.dtype, device=input_.device
22
+ )
23
+ handle = torch.distributed.all_gather_into_tensor(
24
+ output, input_.contiguous(), group=process_group, async_op=async_op
25
+ )
26
+ return output, handle
27
+
28
+
29
+ # Raw operation, does not support autograd, but does support async
30
+ def reduce_scatter_raw(input_: Tensor, process_group: ProcessGroup, async_op: bool = False):
31
+ world_size = torch.distributed.get_world_size(process_group)
32
+ assert input_.shape[0] % world_size == 0
33
+ output = torch.empty(
34
+ input_.shape[0] // world_size, *input_.shape[1:], dtype=input_.dtype, device=input_.device
35
+ )
36
+ handle = torch.distributed.reduce_scatter_tensor(
37
+ output, input_.contiguous(), group=process_group, async_op=async_op
38
+ )
39
+ return output, handle
40
+
41
+
42
+ # Raw operation, does not support autograd, but does support async
43
+ def all_reduce_raw(input_: Tensor, process_group: ProcessGroup, async_op: bool = False):
44
+ input_ = input_.contiguous()
45
+ handle = torch.distributed.all_reduce(input_, group=process_group, async_op=async_op)
46
+ return input_, handle
47
+
48
+
49
+ class AllGatherFunc(torch.autograd.Function):
50
+ """Gather the input from sequence parallel region and concatenate."""
51
+
52
+ @staticmethod
53
+ def forward(ctx, input_: Tensor, process_group: ProcessGroup) -> Tensor:
54
+ ctx.process_group = process_group
55
+ output, _ = all_gather_raw(input_, process_group)
56
+ return output
57
+
58
+ @staticmethod
59
+ def backward(ctx, grad_output: Tensor):
60
+ grad_input, _ = reduce_scatter_raw(grad_output, ctx.process_group)
61
+ return grad_input, None
62
+
63
+
64
+ # Supports autograd, but does not support async
65
+ all_gather = AllGatherFunc.apply
66
+
67
+
68
+ class ReduceScatterFunc(torch.autograd.Function):
69
+ """Reduce scatter the input from the sequence parallel region and concatenate."""
70
+
71
+ @staticmethod
72
+ def forward(ctx, input_: Tensor, process_group: ProcessGroup) -> Tensor:
73
+ ctx.process_group = process_group
74
+ output, _ = reduce_scatter_raw(input_, process_group)
75
+ return output
76
+
77
+ @staticmethod
78
+ def backward(ctx, grad_output: Tensor):
79
+ grad_input, _ = all_gather_raw(grad_output, ctx.process_group)
80
+ return grad_input, None
81
+
82
+
83
+ # Supports autograd, but does not support async
84
+ reduce_scatter = ReduceScatterFunc.apply
85
+
86
+
87
+ class AllReduceFunc(torch.autograd.Function):
88
+ """Gather the input from sequence parallel region and concatenate."""
89
+
90
+ @staticmethod
91
+ def forward(ctx, input_: Tensor, process_group: ProcessGroup) -> Tensor:
92
+ ctx.process_group = process_group
93
+ output, _ = all_reduce_raw(input_, process_group)
94
+ return output
95
+
96
+ @staticmethod
97
+ def backward(ctx, grad_output: Tensor):
98
+ return grad_output, None
99
+
100
+
101
+ # Supports autograd, but does not support async
102
+ all_reduce = AllReduceFunc.apply
103
+
104
+
105
+ def sync_shared_params(model: torch.nn.Module, process_group: ProcessGroup):
106
+ # We want to iterate over parameters with _shared_params=True in the same order,
107
+ # as different ranks might have different number of parameters (e.g., only rank 0 has bias).
108
+ pamams_shared = {
109
+ name: p for name, p in model.named_parameters() if getattr(p, "_shared_params", False)
110
+ }
111
+ for _, p in sorted(pamams_shared.items()):
112
+ with torch.no_grad():
113
+ # Broadcast needs src to be global rank, not group rank
114
+ torch.distributed.broadcast(
115
+ p, src=torch.distributed.get_global_rank(process_group, 0), group=process_group
116
+ )
117
+
118
+
119
+ # Ref: https://github.com/NVIDIA/Megatron-LM/blob/52e636888cccc41e931251c417a7181fc36de926/megatron/optimizer/optimizer.py#L256
120
+ def allreduce_sequence_parallel_grad(model: torch.nn.Module, process_group: ProcessGroup):
121
+ # We want to iterate over parameters with _sequence_parallel=True in the same order,
122
+ # as different ranks might have different number of parameters (e.g., only rank 0 has bias).
123
+ params_seqparallel = {
124
+ name: p for name, p in model.named_parameters() if getattr(p, "_sequence_parallel", False)
125
+ }
126
+ grads = [p.grad for _, p in sorted(params_seqparallel.items())]
127
+ if grads:
128
+ with torch.no_grad():
129
+ coalesced = torch._utils._flatten_dense_tensors(grads)
130
+ torch.distributed.all_reduce(coalesced, group=process_group)
131
+ for buf, synced in zip(grads, torch._utils._unflatten_dense_tensors(coalesced, grads)):
132
+ buf.copy_(synced)
133
+
134
+
135
+ def get_dim_for_local_rank(dim: int, world_size: int, local_rank: int, multiple_of: int = 1) -> int:
136
+ """Get the dim for the local rank derived from splitting dim on world_size processes.
137
+
138
+ The split may not be even across the world_size processes.
139
+ """
140
+ multiple = dim // multiple_of
141
+ div = multiple // world_size
142
+ mod = multiple % world_size
143
+ local_multiple = div + int(local_rank < mod)
144
+ return local_multiple * multiple_of
vendor/mamba-og/mamba_ssm/distributed/tensor_parallel.py ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024, Tri Dao.
2
+ # The TensorParallel linear modules are inspired by https://github.com/NVIDIA/apex/blob/master/apex/transformer/tensor_parallel/layers.py
3
+ from typing import Optional
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.nn.functional as F
8
+ from torch import Tensor
9
+ from torch.distributed import ProcessGroup
10
+ from mamba_ssm.utils.torch import custom_bwd, custom_fwd
11
+
12
+ from einops import rearrange
13
+
14
+ from mamba_ssm.distributed.distributed_utils import (
15
+ all_gather_raw,
16
+ all_reduce,
17
+ all_reduce_raw,
18
+ reduce_scatter,
19
+ reduce_scatter_raw,
20
+ )
21
+
22
+
23
+ class ParallelLinearFunc(torch.autograd.Function):
24
+ @staticmethod
25
+ @custom_fwd
26
+ def forward(ctx, x, weight, bias, process_group=None, sequence_parallel=True):
27
+ """
28
+ If process_group is not None and sequence_parallel=True, we're doing Tensor Parallel
29
+ with sequence parallelism: we do an all_gather_raw of x before doing the matmul.
30
+ """
31
+ ctx.compute_weight_gradient = weight.requires_grad
32
+ ctx.process_group = process_group
33
+ ctx.sequence_parallel = sequence_parallel
34
+
35
+ if torch.is_autocast_enabled():
36
+ x = x.to(dtype=torch.get_autocast_gpu_dtype())
37
+ x = x.contiguous()
38
+ if process_group is not None and sequence_parallel:
39
+ # We want to kick off the all_gather early, before weight dtype conversion
40
+ total_x, handle_x = all_gather_raw(x, process_group, async_op=True)
41
+ else:
42
+ total_x = x
43
+
44
+ if torch.is_autocast_enabled():
45
+ weight = weight.to(dtype=torch.get_autocast_gpu_dtype())
46
+ bias = bias.to(dtype=torch.get_autocast_gpu_dtype()) if bias is not None else None
47
+ weight = weight.contiguous()
48
+ if process_group is not None and sequence_parallel:
49
+ handle_x.wait()
50
+ batch_shape, n = total_x.shape[:-1], total_x.shape[-1]
51
+ batch_dim = batch_shape.numel()
52
+ # https://github.com/pytorch/pytorch/blob/5b51849b48a7dbccd297286cc0110def4706f9e7/aten/src/ATen/native/cuda/Blas.cpp#L174
53
+ output = F.linear(total_x, weight, bias)
54
+ if ctx.compute_weight_gradient:
55
+ ctx.save_for_backward(x, weight)
56
+ else:
57
+ ctx.save_for_backward(weight)
58
+ return output
59
+
60
+ @staticmethod
61
+ @custom_bwd
62
+ def backward(ctx, grad_output):
63
+ grad_output = grad_output.contiguous()
64
+ process_group = ctx.process_group
65
+ sequence_parallel = ctx.sequence_parallel
66
+ if ctx.compute_weight_gradient:
67
+ x, weight = ctx.saved_tensors
68
+ if process_group is not None and sequence_parallel:
69
+ total_x, handle_x = all_gather_raw(x, process_group, async_op=True)
70
+ else:
71
+ total_x = x
72
+ else:
73
+ (weight,) = ctx.saved_tensors
74
+ total_x = None
75
+ batch_shape = grad_output.shape[:-1]
76
+ batch_dim = batch_shape.numel()
77
+ grad_output = grad_output.reshape(batch_dim, grad_output.shape[-1])
78
+ if ctx.needs_input_grad[0]:
79
+ grad_input = F.linear(grad_output, weight.t())
80
+ grad_input = grad_input.reshape(*batch_shape, grad_input.shape[-1])
81
+ if process_group is not None:
82
+ reduce_fn = reduce_scatter_raw if sequence_parallel else all_reduce_raw
83
+ grad_input, handle_grad_input = reduce_fn(grad_input, process_group, async_op=True)
84
+ else:
85
+ grad_input = None
86
+ if ctx.needs_input_grad[1]:
87
+ assert ctx.compute_weight_gradient
88
+ if process_group is not None and sequence_parallel:
89
+ handle_x.wait()
90
+ grad_weight = torch.einsum(
91
+ "bo,bi->oi", grad_output, total_x.reshape(batch_dim, total_x.shape[-1])
92
+ )
93
+ else:
94
+ grad_weight = None
95
+ grad_bias = grad_output.sum(dim=0) if ctx.needs_input_grad[2] else None
96
+ if process_group is not None and ctx.needs_input_grad[0]:
97
+ handle_grad_input.wait()
98
+ return grad_input, grad_weight, grad_bias, None, None
99
+
100
+
101
+ def parallel_linear_func(
102
+ x: Tensor,
103
+ weight: Tensor,
104
+ bias: Optional[Tensor] = None,
105
+ process_group: Optional[ProcessGroup] = None,
106
+ sequence_parallel: bool = True,
107
+ ):
108
+ return ParallelLinearFunc.apply(x, weight, bias, process_group, sequence_parallel)
109
+
110
+
111
+ class ColumnParallelLinear(nn.Linear):
112
+ def __init__(
113
+ self,
114
+ in_features: int,
115
+ out_features: int,
116
+ process_group: ProcessGroup,
117
+ bias: bool = True,
118
+ sequence_parallel=True,
119
+ multiple_of=1,
120
+ device=None,
121
+ dtype=None,
122
+ ) -> None:
123
+ world_size = torch.distributed.get_world_size(process_group)
124
+ if out_features % multiple_of:
125
+ raise ValueError(f"out_features ({out_features}) must be a multiple of {multiple_of}")
126
+ multiple = out_features // multiple_of
127
+ # We want to split @multiple across world_size, but it could be an uneven split
128
+ div = multiple // world_size
129
+ mod = multiple % world_size
130
+ # The first @mod ranks get @div + 1 copies, the rest get @div copies
131
+ local_multiple = div + int(torch.distributed.get_rank(process_group) < mod)
132
+ super().__init__(
133
+ in_features, local_multiple * multiple_of, bias=bias, device=device, dtype=dtype
134
+ )
135
+ self.process_group = process_group
136
+ self.sequence_parallel = sequence_parallel
137
+
138
+ def forward(self, x):
139
+ # If self.sequence_parallel is True, we're doing Tensor Parallel with sequence parallelism:
140
+ # we do an all_gather of x before doing the matmul.
141
+ # If not, then the input is already gathered.
142
+ return parallel_linear_func(
143
+ x,
144
+ self.weight,
145
+ self.bias,
146
+ process_group=self.process_group,
147
+ sequence_parallel=self.sequence_parallel,
148
+ )
149
+
150
+
151
+ class RowParallelLinear(nn.Linear):
152
+ def __init__(
153
+ self,
154
+ in_features: int,
155
+ out_features: int,
156
+ process_group: ProcessGroup,
157
+ bias: bool = True,
158
+ sequence_parallel=True,
159
+ multiple_of=1,
160
+ device=None,
161
+ dtype=None,
162
+ ) -> None:
163
+ world_size = torch.distributed.get_world_size(process_group)
164
+ rank = torch.distributed.get_rank(process_group)
165
+ if in_features % multiple_of:
166
+ raise ValueError(f"in_features ({in_features}) must be a multiple of {multiple_of}")
167
+ multiple = in_features // multiple_of
168
+ # We want to split @multiple across world_size, but it could be an uneven split
169
+ div = multiple // world_size
170
+ mod = multiple % world_size
171
+ # The first @mod ranks get @div + 1 copies, the rest get @div copies
172
+ local_multiple = div + int(torch.distributed.get_rank(process_group) < mod)
173
+ # Only rank 0 will have bias
174
+ super().__init__(
175
+ local_multiple * multiple_of,
176
+ out_features,
177
+ bias=bias and rank == 0,
178
+ device=device,
179
+ dtype=dtype,
180
+ )
181
+ self.process_group = process_group
182
+ self.sequence_parallel = sequence_parallel
183
+
184
+ def forward(self, x):
185
+ """
186
+ We're doing Tensor Parallel with sequence parallelism: we do the matmul and then
187
+ a reduce_scatter of the result.
188
+ """
189
+ out = parallel_linear_func(x, self.weight, self.bias)
190
+ reduce_fn = reduce_scatter if self.sequence_parallel else all_reduce
191
+ return reduce_fn(out, self.process_group)
192
+
193
+
194
+ class VocabParallelEmbedding(nn.Embedding):
195
+ def __init__(self, num_embeddings, *args, process_group=None, padding_idx=None, **kwargs):
196
+ self.process_group = process_group
197
+ if process_group is not None:
198
+ world_size = torch.distributed.get_world_size(process_group)
199
+ if num_embeddings % world_size != 0:
200
+ raise ValueError(
201
+ f"num_embeddings ({num_embeddings}) must be divisible by "
202
+ f"world_size ({world_size})"
203
+ )
204
+ if world_size > 1 and padding_idx is not None:
205
+ raise RuntimeError("ParallelEmbedding does not support padding_idx")
206
+ else:
207
+ world_size = 1
208
+ super().__init__(num_embeddings // world_size, *args, padding_idx=padding_idx, **kwargs)
209
+
210
+ def forward(self, input: Tensor) -> Tensor:
211
+ if self.process_group is None:
212
+ return super().forward(input)
213
+ else:
214
+ rank = torch.distributed.get_rank(self.process_group)
215
+ vocab_size = self.num_embeddings
216
+ vocab_start_index, vocab_end_index = rank * vocab_size, (rank + 1) * vocab_size
217
+ # Create a mask of valid vocab ids (1 means it needs to be masked).
218
+ input_ids_mask = (input < vocab_start_index) | (input >= vocab_end_index)
219
+ input = input - vocab_start_index
220
+ input[input_ids_mask] = 0
221
+ embeddings = super().forward(input)
222
+ embeddings[input_ids_mask] = 0.0
223
+ return embeddings
224
+
225
+
226
+ class ColumnParallelEmbedding(nn.Embedding):
227
+ def __init__(self, num_embeddings, embedding_dim, *args, process_group=None, **kwargs):
228
+ self.process_group = process_group
229
+ if process_group is not None:
230
+ world_size = torch.distributed.get_world_size(process_group)
231
+ if embedding_dim % world_size != 0:
232
+ raise ValueError(
233
+ f"embedding_dim ({embedding_dim}) must be divisible by "
234
+ f"world_size ({world_size})"
235
+ )
236
+ else:
237
+ world_size = 1
238
+ super().__init__(num_embeddings, embedding_dim // world_size, *args, **kwargs)
239
+
240
+
241
+ class ParallelEmbeddings(nn.Module):
242
+ def __init__(
243
+ self,
244
+ embed_dim,
245
+ vocab_size,
246
+ max_position_embeddings,
247
+ process_group,
248
+ padding_idx=None,
249
+ sequence_parallel=True,
250
+ device=None,
251
+ dtype=None,
252
+ ):
253
+ """
254
+ If max_position_embeddings <= 0, there's no position embeddings
255
+ """
256
+ factory_kwargs = {"device": device, "dtype": dtype}
257
+ super().__init__()
258
+ self.process_group = process_group
259
+ self.sequence_parallel = sequence_parallel
260
+ self.word_embeddings = VocabParallelEmbedding(
261
+ vocab_size,
262
+ embed_dim,
263
+ padding_idx=padding_idx,
264
+ process_group=process_group,
265
+ **factory_kwargs,
266
+ )
267
+ self.max_position_embeddings = max_position_embeddings
268
+ if self.max_position_embeddings > 0:
269
+ self.position_embeddings = ColumnParallelEmbedding(
270
+ max_position_embeddings, embed_dim, process_group=process_group, **factory_kwargs
271
+ )
272
+
273
+ def forward(self, input_ids, position_ids=None, combine_batch_seqlen_dim=False):
274
+ """
275
+ input_ids: (batch, seqlen)
276
+ position_ids: (batch, seqlen)
277
+ """
278
+ batch_size, seqlen = input_ids.shape
279
+ world_size = torch.distributed.get_world_size(self.process_group)
280
+ embeddings = self.word_embeddings(input_ids)
281
+ if self.max_position_embeddings > 0:
282
+ if position_ids is None:
283
+ position_ids = torch.arange(seqlen, dtype=torch.long, device=input_ids.device)
284
+ position_embeddings = self.position_embeddings(position_ids)
285
+ if world_size <= 1:
286
+ embeddings = embeddings + position_embeddings
287
+ else:
288
+ partition_dim = self.position_embeddings.embedding_dim
289
+ rank = torch.distributed.get_rank(self.process_group)
290
+ embeddings[
291
+ ..., rank * partition_dim : (rank + 1) * partition_dim
292
+ ] += position_embeddings
293
+ if combine_batch_seqlen_dim:
294
+ embeddings = rearrange(embeddings, "b s d -> (b s) d")
295
+ reduce_fn = reduce_scatter if self.sequence_parallel else all_reduce
296
+ return embeddings if world_size <= 1 else reduce_fn(embeddings, self.process_group)
vendor/mamba-og/mamba_ssm/models/__init__.py ADDED
File without changes
vendor/mamba-og/mamba_ssm/models/config_mamba.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass, field
2
+
3
+
4
+ @dataclass
5
+ class MambaConfig:
6
+
7
+ d_model: int = 2560
8
+ d_intermediate: int = 0
9
+ n_layer: int = 64
10
+ vocab_size: int = 50277
11
+ ssm_cfg: dict = field(default_factory=dict)
12
+ attn_layer_idx: list = field(default_factory=list)
13
+ attn_cfg: dict = field(default_factory=dict)
14
+ rms_norm: bool = True
15
+ residual_in_fp32: bool = True
16
+ fused_add_norm: bool = True
17
+ pad_vocab_size_multiple: int = 8
18
+ tie_embeddings: bool = True
vendor/mamba-og/mamba_ssm/models/mixer_seq_simple.py ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023, Albert Gu, Tri Dao.
2
+
3
+ import math
4
+ from functools import partial
5
+ import json
6
+ import os
7
+ import copy
8
+
9
+ from collections import namedtuple
10
+
11
+ import torch
12
+ import torch.nn as nn
13
+
14
+ from mamba_ssm.models.config_mamba import MambaConfig
15
+ from mamba_ssm.modules.mamba_simple import Mamba
16
+ from mamba_ssm.modules.mamba2 import Mamba2
17
+ from mamba_ssm.modules.mha import MHA
18
+ from mamba_ssm.modules.mlp import GatedMLP
19
+ from mamba_ssm.modules.block import Block
20
+ from mamba_ssm.utils.generation import GenerationMixin
21
+ from mamba_ssm.utils.hf import load_config_hf, load_state_dict_hf
22
+
23
+ try:
24
+ from mamba_ssm.ops.triton.layer_norm import RMSNorm, layer_norm_fn, rms_norm_fn
25
+ except ImportError:
26
+ RMSNorm, layer_norm_fn, rms_norm_fn = None, None, None
27
+
28
+
29
+ def create_block(
30
+ d_model,
31
+ d_intermediate,
32
+ ssm_cfg=None,
33
+ attn_layer_idx=None,
34
+ attn_cfg=None,
35
+ norm_epsilon=1e-5,
36
+ rms_norm=False,
37
+ residual_in_fp32=False,
38
+ fused_add_norm=False,
39
+ layer_idx=None,
40
+ device=None,
41
+ dtype=None,
42
+ ):
43
+ if ssm_cfg is None:
44
+ ssm_cfg = {}
45
+ if attn_layer_idx is None:
46
+ attn_layer_idx = []
47
+ if attn_cfg is None:
48
+ attn_cfg = {}
49
+ factory_kwargs = {"device": device, "dtype": dtype}
50
+ if layer_idx not in attn_layer_idx:
51
+ # Create a copy of the config to modify
52
+ ssm_cfg = copy.deepcopy(ssm_cfg) if ssm_cfg is not None else {}
53
+ ssm_layer = ssm_cfg.pop("layer", "Mamba1")
54
+ if ssm_layer not in ["Mamba1", "Mamba2"]:
55
+ raise ValueError(f"Invalid ssm_layer: {ssm_layer}, only support Mamba1 and Mamba2")
56
+ mixer_cls = partial(
57
+ Mamba2 if ssm_layer == "Mamba2" else Mamba,
58
+ layer_idx=layer_idx,
59
+ **ssm_cfg,
60
+ **factory_kwargs
61
+ )
62
+ else:
63
+ mixer_cls = partial(MHA, layer_idx=layer_idx, **attn_cfg, **factory_kwargs)
64
+ norm_cls = partial(
65
+ nn.LayerNorm if not rms_norm else RMSNorm, eps=norm_epsilon, **factory_kwargs
66
+ )
67
+ if d_intermediate == 0:
68
+ mlp_cls = nn.Identity
69
+ else:
70
+ mlp_cls = partial(
71
+ GatedMLP, hidden_features=d_intermediate, out_features=d_model, **factory_kwargs
72
+ )
73
+ block = Block(
74
+ d_model,
75
+ mixer_cls,
76
+ mlp_cls,
77
+ norm_cls=norm_cls,
78
+ fused_add_norm=fused_add_norm,
79
+ residual_in_fp32=residual_in_fp32,
80
+ )
81
+ block.layer_idx = layer_idx
82
+ return block
83
+
84
+
85
+ # https://github.com/huggingface/transformers/blob/c28d04e9e252a1a099944e325685f14d242ecdcd/src/transformers/models/gpt2/modeling_gpt2.py#L454
86
+ def _init_weights(
87
+ module,
88
+ n_layer,
89
+ initializer_range=0.02, # Now only used for embedding layer.
90
+ rescale_prenorm_residual=True,
91
+ n_residuals_per_layer=1, # Change to 2 if we have MLP
92
+ ):
93
+ if isinstance(module, nn.Linear):
94
+ if module.bias is not None:
95
+ if not getattr(module.bias, "_no_reinit", False):
96
+ nn.init.zeros_(module.bias)
97
+ elif isinstance(module, nn.Embedding):
98
+ nn.init.normal_(module.weight, std=initializer_range)
99
+
100
+ if rescale_prenorm_residual:
101
+ # Reinitialize selected weights subject to the OpenAI GPT-2 Paper Scheme:
102
+ # > A modified initialization which accounts for the accumulation on the residual path with model depth. Scale
103
+ # > the weights of residual layers at initialization by a factor of 1/√N where N is the # of residual layers.
104
+ # > -- GPT-2 :: https://openai.com/blog/better-language-models/
105
+ #
106
+ # Reference (Megatron-LM): https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/model/gpt_model.py
107
+ for name, p in module.named_parameters():
108
+ if name in ["out_proj.weight", "fc2.weight"]:
109
+ # Special Scaled Initialization --> There are 2 Layer Norms per Transformer Block
110
+ # Following Pytorch init, except scale by 1/sqrt(2 * n_layer)
111
+ # We need to reinit p since this code could be called multiple times
112
+ # Having just p *= scale would repeatedly scale it down
113
+ nn.init.kaiming_uniform_(p, a=math.sqrt(5))
114
+ with torch.no_grad():
115
+ p /= math.sqrt(n_residuals_per_layer * n_layer)
116
+
117
+
118
+ class MixerModel(nn.Module):
119
+ def __init__(
120
+ self,
121
+ d_model: int,
122
+ n_layer: int,
123
+ d_intermediate: int,
124
+ vocab_size: int,
125
+ ssm_cfg=None,
126
+ attn_layer_idx=None,
127
+ attn_cfg=None,
128
+ norm_epsilon: float = 1e-5,
129
+ rms_norm: bool = False,
130
+ initializer_cfg=None,
131
+ fused_add_norm=False,
132
+ residual_in_fp32=False,
133
+ device=None,
134
+ dtype=None,
135
+ ) -> None:
136
+ factory_kwargs = {"device": device, "dtype": dtype}
137
+ super().__init__()
138
+ self.residual_in_fp32 = residual_in_fp32
139
+
140
+ self.embedding = nn.Embedding(vocab_size, d_model, **factory_kwargs)
141
+
142
+ # We change the order of residual and layer norm:
143
+ # Instead of LN -> Attn / MLP -> Add, we do:
144
+ # Add -> LN -> Attn / MLP / Mixer, returning both the residual branch (output of Add) and
145
+ # the main branch (output of MLP / Mixer). The model definition is unchanged.
146
+ # This is for performance reason: we can fuse add + layer_norm.
147
+ self.fused_add_norm = fused_add_norm
148
+ if self.fused_add_norm:
149
+ if layer_norm_fn is None or rms_norm_fn is None:
150
+ raise ImportError("Failed to import Triton LayerNorm / RMSNorm kernels")
151
+
152
+ self.layers = nn.ModuleList(
153
+ [
154
+ create_block(
155
+ d_model,
156
+ d_intermediate=d_intermediate,
157
+ ssm_cfg=ssm_cfg,
158
+ attn_layer_idx=attn_layer_idx,
159
+ attn_cfg=attn_cfg,
160
+ norm_epsilon=norm_epsilon,
161
+ rms_norm=rms_norm,
162
+ residual_in_fp32=residual_in_fp32,
163
+ fused_add_norm=fused_add_norm,
164
+ layer_idx=i,
165
+ **factory_kwargs,
166
+ )
167
+ for i in range(n_layer)
168
+ ]
169
+ )
170
+
171
+ self.norm_f = (nn.LayerNorm if not rms_norm else RMSNorm)(
172
+ d_model, eps=norm_epsilon, **factory_kwargs
173
+ )
174
+
175
+ self.apply(
176
+ partial(
177
+ _init_weights,
178
+ n_layer=n_layer,
179
+ **(initializer_cfg if initializer_cfg is not None else {}),
180
+ n_residuals_per_layer=1 if d_intermediate == 0 else 2, # 2 if we have MLP
181
+ )
182
+ )
183
+
184
+ def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
185
+ return {
186
+ i: layer.allocate_inference_cache(batch_size, max_seqlen, dtype=dtype, **kwargs)
187
+ for i, layer in enumerate(self.layers)
188
+ }
189
+
190
+ def forward(self, input_ids, inference_params=None, **mixer_kwargs):
191
+ hidden_states = self.embedding(input_ids)
192
+ residual = None
193
+ for layer in self.layers:
194
+ hidden_states, residual = layer(
195
+ hidden_states, residual, inference_params=inference_params, **mixer_kwargs
196
+ )
197
+ if not self.fused_add_norm:
198
+ residual = (hidden_states + residual) if residual is not None else hidden_states
199
+ hidden_states = self.norm_f(residual.to(dtype=self.norm_f.weight.dtype))
200
+ else:
201
+ # Set prenorm=False here since we don't need the residual
202
+ hidden_states = layer_norm_fn(
203
+ hidden_states,
204
+ self.norm_f.weight,
205
+ self.norm_f.bias,
206
+ eps=self.norm_f.eps,
207
+ residual=residual,
208
+ prenorm=False,
209
+ residual_in_fp32=self.residual_in_fp32,
210
+ is_rms_norm=isinstance(self.norm_f, RMSNorm)
211
+ )
212
+ return hidden_states
213
+
214
+
215
+ class MambaLMHeadModel(nn.Module, GenerationMixin):
216
+
217
+ def __init__(
218
+ self,
219
+ config: MambaConfig,
220
+ initializer_cfg=None,
221
+ device=None,
222
+ dtype=None,
223
+ ) -> None:
224
+ self.config = config
225
+ d_model = config.d_model
226
+ n_layer = config.n_layer
227
+ d_intermediate = config.d_intermediate
228
+ vocab_size = config.vocab_size
229
+ ssm_cfg = config.ssm_cfg
230
+ attn_layer_idx = config.attn_layer_idx
231
+ attn_cfg = config.attn_cfg
232
+ rms_norm = config.rms_norm
233
+ residual_in_fp32 = config.residual_in_fp32
234
+ fused_add_norm = config.fused_add_norm
235
+ pad_vocab_size_multiple = config.pad_vocab_size_multiple
236
+ factory_kwargs = {"device": device, "dtype": dtype}
237
+
238
+ super().__init__()
239
+ if vocab_size % pad_vocab_size_multiple != 0:
240
+ vocab_size += pad_vocab_size_multiple - (vocab_size % pad_vocab_size_multiple)
241
+ self.backbone = MixerModel(
242
+ d_model=d_model,
243
+ n_layer=n_layer,
244
+ d_intermediate=d_intermediate,
245
+ vocab_size=vocab_size,
246
+ ssm_cfg=ssm_cfg,
247
+ attn_layer_idx=attn_layer_idx,
248
+ attn_cfg=attn_cfg,
249
+ rms_norm=rms_norm,
250
+ initializer_cfg=initializer_cfg,
251
+ fused_add_norm=fused_add_norm,
252
+ residual_in_fp32=residual_in_fp32,
253
+ **factory_kwargs,
254
+ )
255
+ self.lm_head = nn.Linear(d_model, vocab_size, bias=False, **factory_kwargs)
256
+
257
+ # Initialize weights and apply final processing
258
+ self.apply(
259
+ partial(
260
+ _init_weights,
261
+ n_layer=n_layer,
262
+ **(initializer_cfg if initializer_cfg is not None else {}),
263
+ )
264
+ )
265
+ self.tie_weights()
266
+
267
+ def tie_weights(self):
268
+ if self.config.tie_embeddings:
269
+ self.lm_head.weight = self.backbone.embedding.weight
270
+
271
+ def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
272
+ return self.backbone.allocate_inference_cache(batch_size, max_seqlen, dtype=dtype, **kwargs)
273
+
274
+ def forward(self, input_ids, position_ids=None, inference_params=None, num_last_tokens=0, **mixer_kwargs):
275
+ """
276
+ "position_ids" is just to be compatible with Transformer generation. We don't use it.
277
+ num_last_tokens: if > 0, only return the logits for the last n tokens
278
+ """
279
+ hidden_states = self.backbone(input_ids, inference_params=inference_params, **mixer_kwargs)
280
+ if num_last_tokens > 0:
281
+ hidden_states = hidden_states[:, -num_last_tokens:]
282
+ lm_logits = self.lm_head(hidden_states)
283
+ CausalLMOutput = namedtuple("CausalLMOutput", ["logits"])
284
+ return CausalLMOutput(logits=lm_logits)
285
+
286
+ @classmethod
287
+ def from_pretrained(cls, pretrained_model_name, device=None, dtype=None, **kwargs):
288
+ config_data = load_config_hf(pretrained_model_name)
289
+ config = MambaConfig(**config_data)
290
+ model = cls(config, device=device, dtype=dtype, **kwargs)
291
+ model.load_state_dict(load_state_dict_hf(pretrained_model_name, device=device, dtype=dtype))
292
+ return model
293
+
294
+ def save_pretrained(self, save_directory):
295
+ """
296
+ Minimal implementation of save_pretrained for MambaLMHeadModel.
297
+ Save the model and its configuration file to a directory.
298
+ """
299
+ # Ensure save_directory exists
300
+ os.makedirs(save_directory, exist_ok=True)
301
+
302
+ # Save the model's state_dict
303
+ model_path = os.path.join(save_directory, 'pytorch_model.bin')
304
+ torch.save(self.state_dict(), model_path)
305
+
306
+ # Save the configuration of the model
307
+ config_path = os.path.join(save_directory, 'config.json')
308
+ with open(config_path, 'w') as f:
309
+ json.dump(self.config.__dict__, f, indent=4)
vendor/mamba-og/mamba_ssm/modules/__init__.py ADDED
File without changes
vendor/mamba-og/mamba_ssm/modules/block.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024, Tri Dao, Albert Gu.
2
+ from typing import Optional
3
+
4
+ import torch
5
+ from torch import nn, Tensor
6
+
7
+ from mamba_ssm.ops.triton.layer_norm import RMSNorm, layer_norm_fn
8
+
9
+
10
+ class Block(nn.Module):
11
+ def __init__(
12
+ self, dim, mixer_cls, mlp_cls, norm_cls=nn.LayerNorm, fused_add_norm=False, residual_in_fp32=False
13
+ ):
14
+ """
15
+ Simple block wrapping a mixer class with LayerNorm/RMSNorm and residual connection"
16
+
17
+ This Block has a slightly different structure compared to a regular
18
+ prenorm Transformer block.
19
+ The standard block is: LN -> MHA/MLP -> Add.
20
+ [Ref: https://arxiv.org/abs/2002.04745]
21
+ Here we have: Add -> LN -> Mixer, returning both
22
+ the hidden_states (output of the mixer) and the residual.
23
+ This is purely for performance reasons, as we can fuse add and LayerNorm.
24
+ The residual needs to be provided (except for the very first block).
25
+ """
26
+ super().__init__()
27
+ self.residual_in_fp32 = residual_in_fp32
28
+ self.fused_add_norm = fused_add_norm
29
+ self.norm = norm_cls(dim)
30
+ self.mixer = mixer_cls(dim)
31
+ if mlp_cls is not nn.Identity:
32
+ self.norm2 = norm_cls(dim)
33
+ self.mlp = mlp_cls(dim)
34
+ else:
35
+ self.mlp = None
36
+ if self.fused_add_norm:
37
+ assert RMSNorm is not None, "RMSNorm import fails"
38
+ assert isinstance(
39
+ self.norm, (nn.LayerNorm, RMSNorm)
40
+ ), "Only LayerNorm and RMSNorm are supported for fused_add_norm"
41
+
42
+ def forward(
43
+ self, hidden_states: Tensor, residual: Optional[Tensor] = None, inference_params=None, **mixer_kwargs
44
+ ):
45
+ r"""Pass the input through the encoder layer.
46
+
47
+ Args:
48
+ hidden_states: the sequence to the encoder layer (required).
49
+ residual: hidden_states = Mixer(LN(residual))
50
+ """
51
+ if not self.fused_add_norm:
52
+ residual = (hidden_states + residual) if residual is not None else hidden_states
53
+ hidden_states = self.norm(residual.to(dtype=self.norm.weight.dtype))
54
+ if self.residual_in_fp32:
55
+ residual = residual.to(torch.float32)
56
+ else:
57
+ hidden_states, residual = layer_norm_fn(
58
+ hidden_states,
59
+ self.norm.weight,
60
+ self.norm.bias,
61
+ residual=residual,
62
+ prenorm=True,
63
+ residual_in_fp32=self.residual_in_fp32,
64
+ eps=self.norm.eps,
65
+ is_rms_norm=isinstance(self.norm, RMSNorm)
66
+ )
67
+ hidden_states = self.mixer(hidden_states, inference_params=inference_params, **mixer_kwargs)
68
+
69
+ if self.mlp is not None:
70
+ if not self.fused_add_norm:
71
+ residual = hidden_states + residual
72
+ hidden_states = self.norm2(residual.to(dtype=self.norm2.weight.dtype))
73
+ if self.residual_in_fp32:
74
+ residual = residual.to(torch.float32)
75
+ else:
76
+ hidden_states, residual = layer_norm_fn(
77
+ hidden_states,
78
+ self.norm2.weight,
79
+ self.norm2.bias,
80
+ residual=residual,
81
+ prenorm=True,
82
+ residual_in_fp32=self.residual_in_fp32,
83
+ eps=self.norm2.eps,
84
+ is_rms_norm=isinstance(self.norm2, RMSNorm)
85
+ )
86
+ hidden_states = self.mlp(hidden_states)
87
+
88
+ return hidden_states, residual
89
+
90
+ def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
91
+ return self.mixer.allocate_inference_cache(batch_size, max_seqlen, dtype=dtype, **kwargs)
vendor/mamba-og/mamba_ssm/modules/mamba2.py ADDED
@@ -0,0 +1,383 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024, Tri Dao, Albert Gu.
2
+
3
+ import math
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.nn.functional as F
8
+
9
+ from einops import rearrange, repeat
10
+
11
+ try:
12
+ from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
13
+ except ImportError:
14
+ causal_conv1d_fn, causal_conv1d_update = None, None
15
+
16
+ try:
17
+ from causal_conv1d.causal_conv1d_varlen import causal_conv1d_varlen_states
18
+ except ImportError:
19
+ causal_conv1d_varlen_states = None
20
+
21
+ try:
22
+ from mamba_ssm.ops.triton.selective_state_update import selective_state_update
23
+ except ImportError:
24
+ selective_state_update = None
25
+
26
+ from mamba_ssm.ops.triton.layernorm_gated import RMSNorm as RMSNormGated
27
+
28
+ from mamba_ssm.distributed.tensor_parallel import ColumnParallelLinear, RowParallelLinear
29
+ from mamba_ssm.distributed.distributed_utils import all_reduce, reduce_scatter
30
+
31
+ from mamba_ssm.ops.triton.ssd_combined import mamba_chunk_scan_combined
32
+ from mamba_ssm.ops.triton.ssd_combined import mamba_split_conv1d_scan_combined
33
+
34
+ from huggingface_hub import PyTorchModelHubMixin
35
+
36
+
37
+ class Mamba2(nn.Module, PyTorchModelHubMixin):
38
+ def __init__(
39
+ self,
40
+ d_model,
41
+ d_state=128,
42
+ d_conv=4,
43
+ conv_init=None,
44
+ expand=2,
45
+ headdim=64,
46
+ d_ssm=None, # If not None, we only apply SSM on this many dimensions, the rest uses gated MLP
47
+ ngroups=1,
48
+ A_init_range=(1, 16),
49
+ D_has_hdim=False,
50
+ rmsnorm=True,
51
+ norm_before_gate=False,
52
+ dt_min=0.001,
53
+ dt_max=0.1,
54
+ dt_init_floor=1e-4,
55
+ dt_limit=(0.0, float("inf")),
56
+ bias=False,
57
+ conv_bias=True,
58
+ # Fused kernel and sharding options
59
+ chunk_size=256,
60
+ use_mem_eff_path=True,
61
+ layer_idx=None, # Absorb kwarg for general module
62
+ process_group=None,
63
+ sequence_parallel=True,
64
+ device=None,
65
+ dtype=None,
66
+ ):
67
+ factory_kwargs = {"device": device, "dtype": dtype}
68
+ super().__init__()
69
+ self.d_model = d_model
70
+ self.d_state = d_state
71
+ self.d_conv = d_conv
72
+ self.conv_init = conv_init
73
+ self.expand = expand
74
+ self.process_group = process_group
75
+ self.sequence_parallel = sequence_parallel
76
+ self.world_size = 1 if process_group is None else process_group.size()
77
+ self.local_rank = 0 if process_group is None else process_group.rank()
78
+ self.d_inner = (self.expand * self.d_model) // self.world_size
79
+ assert self.d_inner * self.world_size == self.expand * self.d_model
80
+ self.headdim = headdim
81
+ self.d_ssm = self.d_inner if d_ssm is None else d_ssm // self.world_size
82
+ assert ngroups % self.world_size == 0
83
+ self.ngroups = ngroups // self.world_size
84
+ assert self.d_ssm % self.headdim == 0
85
+ self.nheads = self.d_ssm // self.headdim
86
+ self.D_has_hdim = D_has_hdim
87
+ self.rmsnorm = rmsnorm
88
+ self.norm_before_gate = norm_before_gate
89
+ self.dt_limit = dt_limit
90
+ self.activation = "silu"
91
+ self.chunk_size = chunk_size
92
+ self.use_mem_eff_path = use_mem_eff_path
93
+ self.layer_idx = layer_idx
94
+
95
+ # Order: [z, x, B, C, dt]
96
+ d_in_proj = 2 * self.d_inner + 2 * self.ngroups * self.d_state + self.nheads
97
+ if self.process_group is None:
98
+ self.in_proj = nn.Linear(self.d_model, d_in_proj, bias=bias, **factory_kwargs)
99
+ else:
100
+ self.in_proj = ColumnParallelLinear(self.d_model, d_in_proj * self.world_size, bias=bias,
101
+ process_group=self.process_group, sequence_parallel=self.sequence_parallel,
102
+ **factory_kwargs)
103
+
104
+ conv_dim = self.d_ssm + 2 * self.ngroups * self.d_state
105
+ self.conv1d = nn.Conv1d(
106
+ in_channels=conv_dim,
107
+ out_channels=conv_dim,
108
+ bias=conv_bias,
109
+ kernel_size=d_conv,
110
+ groups=conv_dim,
111
+ padding=d_conv - 1,
112
+ **factory_kwargs,
113
+ )
114
+ if self.conv_init is not None:
115
+ nn.init.uniform_(self.conv1d.weight, -self.conv_init, self.conv_init)
116
+
117
+ self.act = nn.SiLU()
118
+
119
+ # Initialize log dt bias
120
+ dt = torch.exp(
121
+ torch.rand(self.nheads, **factory_kwargs) * (math.log(dt_max) - math.log(dt_min))
122
+ + math.log(dt_min)
123
+ )
124
+ dt = torch.clamp(dt, min=dt_init_floor)
125
+ # Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
126
+ inv_dt = dt + torch.log(-torch.expm1(-dt))
127
+ self.dt_bias = nn.Parameter(inv_dt)
128
+ # Just to be explicit. Without this we already don't put wd on dt_bias because of the check
129
+ # name.endswith("bias") in param_grouping.py
130
+ self.dt_bias._no_weight_decay = True
131
+
132
+ assert A_init_range[0] > 0 and A_init_range[1] >= A_init_range[0]
133
+ A = torch.empty(self.nheads, dtype=torch.float32, device=device).uniform_(*A_init_range)
134
+ A_log = torch.log(A).to(dtype=dtype)
135
+ self.A_log = nn.Parameter(A_log)
136
+ self.A_log._no_weight_decay = True
137
+
138
+ # D "skip" parameter
139
+ self.D = nn.Parameter(torch.ones(self.d_ssm if self.D_has_hdim else self.nheads, device=device))
140
+ self.D._no_weight_decay = True
141
+
142
+ if self.rmsnorm:
143
+ assert RMSNormGated is not None
144
+ self.norm = RMSNormGated(self.d_ssm, eps=1e-5, norm_before_gate=self.norm_before_gate,
145
+ group_size=self.d_ssm // ngroups, **factory_kwargs)
146
+
147
+ if self.process_group is None:
148
+ self.out_proj = nn.Linear(self.d_inner, self.d_model, bias=bias, **factory_kwargs)
149
+ else:
150
+ self.out_proj = RowParallelLinear(self.d_inner * self.world_size, self.d_model, bias=bias,
151
+ process_group=self.process_group, sequence_parallel=self.sequence_parallel,
152
+ **factory_kwargs)
153
+
154
+ def forward(self, u, seqlen=None, seq_idx=None, cu_seqlens=None, inference_params=None):
155
+ """
156
+ u: (batch, seqlen, hidden_dim) if seqlen=None.
157
+ If seqlen is not None, u is (batch * seqlen, hidden_dim). This is so that when we
158
+ split u during sequence parallel, we split the batch * seqlen dimension
159
+ (in case batch is small).
160
+ Returns: same shape as u
161
+ """
162
+ seqlen_og = seqlen
163
+ if seqlen is None:
164
+ batch, seqlen, dim = u.shape
165
+ else:
166
+ batch_seqlen, dim = u.shape
167
+ batch = batch_seqlen // seqlen
168
+
169
+ conv_state, ssm_state = None, None
170
+ if inference_params is not None:
171
+ inference_batch = cu_seqlens.shape[0] - 1 if cu_seqlens is not None else batch
172
+ conv_state, ssm_state = self._get_states_from_cache(inference_params, inference_batch)
173
+ if inference_params.seqlen_offset > 0:
174
+ # The states are updated inplace
175
+ out, _, _ = self.step(u, conv_state, ssm_state)
176
+ return out
177
+
178
+ zxbcdt = self.in_proj(u) # (B, L, d_in_proj) or (B * L, d_in_proj)
179
+ if seqlen_og is not None:
180
+ zxbcdt = rearrange(zxbcdt, "(b l) d -> b l d", l=seqlen)
181
+ # If the model is loaded in fp16, without the .float() here, A might be -inf
182
+ A = -torch.exp(self.A_log.float()) # (nheads) or (d_inner, d_state)
183
+ dt_limit_kwargs = {} if self.dt_limit == (0.0, float("inf")) else dict(dt_limit=self.dt_limit)
184
+ if self.use_mem_eff_path and inference_params is None:
185
+ out = mamba_split_conv1d_scan_combined(
186
+ zxbcdt,
187
+ rearrange(self.conv1d.weight, "d 1 w -> d w"),
188
+ self.conv1d.bias,
189
+ self.dt_bias,
190
+ A,
191
+ D=rearrange(self.D, "(h p) -> h p", p=self.headdim) if self.D_has_hdim else self.D,
192
+ chunk_size=self.chunk_size,
193
+ seq_idx=seq_idx,
194
+ activation=self.activation,
195
+ rmsnorm_weight=self.norm.weight if self.rmsnorm else None,
196
+ rmsnorm_eps=self.norm.eps if self.rmsnorm else 1e-6,
197
+ outproj_weight=self.out_proj.weight,
198
+ outproj_bias=self.out_proj.bias,
199
+ headdim=None if self.D_has_hdim else self.headdim,
200
+ ngroups=self.ngroups,
201
+ norm_before_gate=self.norm_before_gate,
202
+ **dt_limit_kwargs,
203
+ )
204
+ if seqlen_og is not None:
205
+ out = rearrange(out, "b l d -> (b l) d")
206
+ if self.process_group is not None:
207
+ reduce_fn = reduce_scatter if self.sequence_parallel else all_reduce
208
+ out = reduce_fn(out, self.process_group)
209
+ else:
210
+ d_mlp = (zxbcdt.shape[-1] - 2 * self.d_ssm - 2 * self.ngroups * self.d_state - self.nheads) // 2
211
+ z0, x0, z, xBC, dt = torch.split(
212
+ zxbcdt,
213
+ [d_mlp, d_mlp, self.d_ssm, self.d_ssm + 2 * self.ngroups * self.d_state, self.nheads],
214
+ dim=-1
215
+ )
216
+ if conv_state is not None:
217
+ if cu_seqlens is None:
218
+ # If we just take xBC[:, :, -self.d_conv :], it will error if seqlen < self.d_conv
219
+ # Instead F.pad will pad with zeros if seqlen < self.d_conv, and truncate otherwise.
220
+ xBC_t = rearrange(xBC, "b l d -> b d l")
221
+ conv_state.copy_(F.pad(xBC_t, (self.d_conv - xBC_t.shape[-1], 0))) # Update state (B D W)
222
+ else:
223
+ assert causal_conv1d_varlen_states is not None, "varlen inference requires causal_conv1d package"
224
+ assert batch == 1, "varlen inference only supports batch dimension 1"
225
+ conv_varlen_states = causal_conv1d_varlen_states(
226
+ xBC.squeeze(0), cu_seqlens, state_len=conv_state.shape[-1]
227
+ )
228
+ conv_state.copy_(conv_varlen_states)
229
+ assert self.activation in ["silu", "swish"]
230
+ if causal_conv1d_fn is None or self.activation not in ["silu", "swish"]:
231
+ assert seq_idx is None, "varlen conv1d requires the causal_conv1d package"
232
+ xBC = self.act(
233
+ self.conv1d(xBC.transpose(1, 2)).transpose(1, 2)[:, :-(self.d_conv - 1)]
234
+ ) # (B, L, self.d_ssm + 2 * ngroups * d_state)
235
+ else:
236
+ xBC = causal_conv1d_fn(
237
+ xBC.transpose(1, 2),
238
+ rearrange(self.conv1d.weight, "d 1 w -> d w"),
239
+ bias=self.conv1d.bias,
240
+ activation=self.activation,
241
+ seq_idx=seq_idx,
242
+ ).transpose(1, 2)
243
+ x, B, C = torch.split(xBC, [self.d_ssm, self.ngroups * self.d_state, self.ngroups * self.d_state], dim=-1)
244
+ y = mamba_chunk_scan_combined(
245
+ rearrange(x, "b l (h p) -> b l h p", p=self.headdim),
246
+ dt,
247
+ A,
248
+ rearrange(B, "b l (g n) -> b l g n", g=self.ngroups),
249
+ rearrange(C, "b l (g n) -> b l g n", g=self.ngroups),
250
+ chunk_size=self.chunk_size,
251
+ D=rearrange(self.D, "(h p) -> h p", p=self.headdim) if self.D_has_hdim else self.D,
252
+ z=rearrange(z, "b l (h p) -> b l h p", p=self.headdim) if not self.rmsnorm else None,
253
+ dt_bias=self.dt_bias,
254
+ dt_softplus=True,
255
+ seq_idx=seq_idx,
256
+ cu_seqlens=cu_seqlens,
257
+ **dt_limit_kwargs,
258
+ return_final_states=ssm_state is not None,
259
+ return_varlen_states=cu_seqlens is not None and inference_params is not None,
260
+ )
261
+ if ssm_state is not None:
262
+ y, last_state, *rest = y
263
+ if cu_seqlens is None:
264
+ ssm_state.copy_(last_state)
265
+ else:
266
+ varlen_states = rest[0]
267
+ ssm_state.copy_(varlen_states)
268
+ y = rearrange(y, "b l h p -> b l (h p)")
269
+ if self.rmsnorm:
270
+ y = self.norm(y, z)
271
+ if d_mlp > 0:
272
+ y = torch.cat([F.silu(z0) * x0, y], dim=-1)
273
+ if seqlen_og is not None:
274
+ y = rearrange(y, "b l d -> (b l) d")
275
+ out = self.out_proj(y)
276
+ return out
277
+
278
+ def step(self, hidden_states, conv_state, ssm_state):
279
+ dtype = hidden_states.dtype
280
+ assert hidden_states.shape[1] == 1, "Only support decoding with 1 token at a time for now"
281
+ zxbcdt = self.in_proj(hidden_states.squeeze(1)) # (B 2D)
282
+ d_mlp = (zxbcdt.shape[-1] - 2 * self.d_ssm - 2 * self.ngroups * self.d_state - self.nheads) // 2
283
+ z0, x0, z, xBC, dt = torch.split(
284
+ zxbcdt,
285
+ [d_mlp, d_mlp, self.d_ssm, self.d_ssm + 2 * self.ngroups * self.d_state, self.nheads],
286
+ dim=-1
287
+ )
288
+
289
+ # Conv step
290
+ if causal_conv1d_update is None:
291
+ conv_state.copy_(torch.roll(conv_state, shifts=-1, dims=-1)) # Update state (B D W)
292
+ conv_state[:, :, -1] = xBC
293
+ xBC = torch.sum(conv_state * rearrange(self.conv1d.weight, "d 1 w -> d w"), dim=-1) # (B D)
294
+ if self.conv1d.bias is not None:
295
+ xBC = xBC + self.conv1d.bias
296
+ xBC = self.act(xBC).to(dtype=dtype)
297
+ else:
298
+ xBC = causal_conv1d_update(
299
+ xBC,
300
+ conv_state,
301
+ rearrange(self.conv1d.weight, "d 1 w -> d w"),
302
+ self.conv1d.bias,
303
+ self.activation,
304
+ )
305
+
306
+ x, B, C = torch.split(xBC, [self.d_ssm, self.ngroups * self.d_state, self.ngroups * self.d_state], dim=-1)
307
+ A = -torch.exp(self.A_log.float()) # (nheads,)
308
+
309
+ # SSM step
310
+ if selective_state_update is None:
311
+ assert self.ngroups == 1, "Only support ngroups=1 for this inference code path"
312
+ # Discretize A and B
313
+ dt = F.softplus(dt + self.dt_bias.to(dtype=dt.dtype)) # (batch, nheads)
314
+ dA = torch.exp(dt * A) # (batch, nheads)
315
+ x = rearrange(x, "b (h p) -> b h p", p=self.headdim)
316
+ dBx = torch.einsum("bh,bn,bhp->bhpn", dt, B, x)
317
+ ssm_state.copy_(ssm_state * rearrange(dA, "b h -> b h 1 1") + dBx)
318
+ y = torch.einsum("bhpn,bn->bhp", ssm_state.to(dtype), C)
319
+ y = y + rearrange(self.D.to(dtype), "h -> h 1") * x
320
+ y = rearrange(y, "b h p -> b (h p)")
321
+ if not self.rmsnorm:
322
+ y = y * self.act(z) # (B D)
323
+ else:
324
+ A = repeat(A, "h -> h p n", p=self.headdim, n=self.d_state).to(dtype=torch.float32)
325
+ dt = repeat(dt, "b h -> b h p", p=self.headdim)
326
+ dt_bias = repeat(self.dt_bias, "h -> h p", p=self.headdim)
327
+ D = repeat(self.D, "h -> h p", p=self.headdim)
328
+ B = rearrange(B, "b (g n) -> b g n", g=self.ngroups)
329
+ C = rearrange(C, "b (g n) -> b g n", g=self.ngroups)
330
+ x_reshaped = rearrange(x, "b (h p) -> b h p", p=self.headdim)
331
+ if not self.rmsnorm:
332
+ z = rearrange(z, "b (h p) -> b h p", p=self.headdim)
333
+ y = selective_state_update(
334
+ ssm_state, x_reshaped, dt, A, B, C, D, z=z if not self.rmsnorm else None,
335
+ dt_bias=dt_bias, dt_softplus=True
336
+ )
337
+ y = rearrange(y, "b h p -> b (h p)")
338
+ if self.rmsnorm:
339
+ y = self.norm(y, z)
340
+ if d_mlp > 0:
341
+ y = torch.cat([F.silu(z0) * x0, y], dim=-1)
342
+ out = self.out_proj(y)
343
+ return out.unsqueeze(1), conv_state, ssm_state
344
+
345
+ def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
346
+ device = self.out_proj.weight.device
347
+ conv_dtype = self.conv1d.weight.dtype if dtype is None else dtype
348
+ conv_state = torch.zeros(
349
+ batch_size, self.d_conv, self.conv1d.weight.shape[0], device=device, dtype=conv_dtype
350
+ ).transpose(1, 2)
351
+ ssm_dtype = self.in_proj.weight.dtype if dtype is None else dtype
352
+ ssm_state = torch.zeros(
353
+ batch_size, self.nheads, self.headdim, self.d_state, device=device, dtype=ssm_dtype
354
+ )
355
+ return conv_state, ssm_state
356
+
357
+ def _get_states_from_cache(self, inference_params, batch_size, initialize_states=False):
358
+ assert self.layer_idx is not None
359
+ if self.layer_idx not in inference_params.key_value_memory_dict:
360
+ batch_shape = (batch_size,)
361
+ conv_state = torch.zeros(
362
+ batch_size,
363
+ self.d_conv,
364
+ self.conv1d.weight.shape[0],
365
+ device=self.conv1d.weight.device,
366
+ dtype=self.conv1d.weight.dtype,
367
+ ).transpose(1, 2)
368
+ ssm_state = torch.zeros(
369
+ batch_size,
370
+ self.nheads,
371
+ self.headdim,
372
+ self.d_state,
373
+ device=self.in_proj.weight.device,
374
+ dtype=self.in_proj.weight.dtype,
375
+ )
376
+ inference_params.key_value_memory_dict[self.layer_idx] = (conv_state, ssm_state)
377
+ else:
378
+ conv_state, ssm_state = inference_params.key_value_memory_dict[self.layer_idx]
379
+ # TODO: What if batch size changes between generation, and we reuse the same states?
380
+ if initialize_states:
381
+ conv_state.zero_()
382
+ ssm_state.zero_()
383
+ return conv_state, ssm_state
vendor/mamba-og/mamba_ssm/modules/mamba2_simple.py ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024, Tri Dao, Albert Gu.
2
+
3
+ import math
4
+ import torch
5
+ import torch.nn as nn
6
+ import torch.nn.functional as F
7
+
8
+ from einops import rearrange, repeat
9
+
10
+ try:
11
+ from causal_conv1d import causal_conv1d_fn
12
+ except ImportError:
13
+ causal_conv1d_fn = None
14
+
15
+ try:
16
+ from mamba_ssm.ops.triton.layernorm_gated import RMSNorm as RMSNormGated, LayerNorm
17
+ except ImportError:
18
+ RMSNormGated, LayerNorm = None, None
19
+
20
+ from mamba_ssm.ops.triton.ssd_combined import mamba_chunk_scan_combined
21
+ from mamba_ssm.ops.triton.ssd_combined import mamba_split_conv1d_scan_combined
22
+
23
+
24
+ class Mamba2Simple(nn.Module):
25
+ def __init__(
26
+ self,
27
+ d_model,
28
+ d_state=64,
29
+ d_conv=4,
30
+ conv_init=None,
31
+ expand=2,
32
+ headdim=128,
33
+ ngroups=1,
34
+ A_init_range=(1, 16),
35
+ dt_min=0.001,
36
+ dt_max=0.1,
37
+ dt_init_floor=1e-4,
38
+ dt_limit=(0.0, float("inf")),
39
+ learnable_init_states=False,
40
+ activation="swish",
41
+ bias=False,
42
+ conv_bias=True,
43
+ # Fused kernel and sharding options
44
+ chunk_size=256,
45
+ use_mem_eff_path=True,
46
+ layer_idx=None, # Absorb kwarg for general module
47
+ device=None,
48
+ dtype=None,
49
+ ):
50
+ factory_kwargs = {"device": device, "dtype": dtype}
51
+ super().__init__()
52
+ self.d_model = d_model
53
+ self.d_state = d_state
54
+ self.d_conv = d_conv
55
+ self.conv_init = conv_init
56
+ self.expand = expand
57
+ self.d_inner = self.expand * self.d_model
58
+ self.headdim = headdim
59
+ self.ngroups = ngroups
60
+ assert self.d_inner % self.headdim == 0
61
+ self.nheads = self.d_inner // self.headdim
62
+ self.dt_limit = dt_limit
63
+ self.learnable_init_states = learnable_init_states
64
+ self.activation = activation
65
+ self.chunk_size = chunk_size
66
+ self.use_mem_eff_path = use_mem_eff_path
67
+ self.layer_idx = layer_idx
68
+
69
+ # Order: [z, x, B, C, dt]
70
+ d_in_proj = 2 * self.d_inner + 2 * self.ngroups * self.d_state + self.nheads
71
+ self.in_proj = nn.Linear(self.d_model, d_in_proj, bias=bias, **factory_kwargs)
72
+
73
+ conv_dim = self.d_inner + 2 * self.ngroups * self.d_state
74
+ self.conv1d = nn.Conv1d(
75
+ in_channels=conv_dim,
76
+ out_channels=conv_dim,
77
+ bias=conv_bias,
78
+ kernel_size=d_conv,
79
+ groups=conv_dim,
80
+ padding=d_conv - 1,
81
+ **factory_kwargs,
82
+ )
83
+ if self.conv_init is not None:
84
+ nn.init.uniform_(self.conv1d.weight, -self.conv_init, self.conv_init)
85
+ # self.conv1d.weight._no_weight_decay = True
86
+
87
+ if self.learnable_init_states:
88
+ self.init_states = nn.Parameter(torch.zeros(self.nheads, self.headdim, self.d_state, **factory_kwargs))
89
+ self.init_states._no_weight_decay = True
90
+
91
+ self.act = nn.SiLU()
92
+
93
+ # Initialize log dt bias
94
+ dt = torch.exp(
95
+ torch.rand(self.nheads, **factory_kwargs) * (math.log(dt_max) - math.log(dt_min))
96
+ + math.log(dt_min)
97
+ )
98
+ dt = torch.clamp(dt, min=dt_init_floor)
99
+ # Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
100
+ inv_dt = dt + torch.log(-torch.expm1(-dt))
101
+ self.dt_bias = nn.Parameter(inv_dt)
102
+ # Just to be explicit. Without this we already don't put wd on dt_bias because of the check
103
+ # name.endswith("bias") in param_grouping.py
104
+ self.dt_bias._no_weight_decay = True
105
+
106
+ # A parameter
107
+ assert A_init_range[0] > 0 and A_init_range[1] >= A_init_range[0]
108
+ A = torch.empty(self.nheads, dtype=torch.float32, device=device).uniform_(*A_init_range)
109
+ A_log = torch.log(A).to(dtype=dtype)
110
+ self.A_log = nn.Parameter(A_log)
111
+ # self.register_buffer("A_log", torch.zeros(self.nheads, dtype=torch.float32, device=device), persistent=True)
112
+ self.A_log._no_weight_decay = True
113
+
114
+ # D "skip" parameter
115
+ self.D = nn.Parameter(torch.ones(self.nheads, device=device))
116
+ self.D._no_weight_decay = True
117
+
118
+ # Extra normalization layer right before output projection
119
+ assert RMSNormGated is not None
120
+ self.norm = RMSNormGated(self.d_inner, eps=1e-5, norm_before_gate=False, **factory_kwargs)
121
+
122
+ self.out_proj = nn.Linear(self.d_inner, self.d_model, bias=bias, **factory_kwargs)
123
+
124
+ def forward(self, u, seq_idx=None):
125
+ """
126
+ u: (B, L, D)
127
+ Returns: same shape as u
128
+ """
129
+ batch, seqlen, dim = u.shape
130
+
131
+ zxbcdt = self.in_proj(u) # (B, L, d_in_proj)
132
+ A = -torch.exp(self.A_log) # (nheads) or (d_inner, d_state)
133
+ initial_states=repeat(self.init_states, "... -> b ...", b=batch) if self.learnable_init_states else None
134
+ dt_limit_kwargs = {} if self.dt_limit == (0.0, float("inf")) else dict(dt_limit=self.dt_limit)
135
+
136
+ if self.use_mem_eff_path:
137
+ # Fully fused path
138
+ out = mamba_split_conv1d_scan_combined(
139
+ zxbcdt,
140
+ rearrange(self.conv1d.weight, "d 1 w -> d w"),
141
+ self.conv1d.bias,
142
+ self.dt_bias,
143
+ A,
144
+ D=self.D,
145
+ chunk_size=self.chunk_size,
146
+ seq_idx=seq_idx,
147
+ activation=self.activation,
148
+ rmsnorm_weight=self.norm.weight,
149
+ rmsnorm_eps=self.norm.eps,
150
+ outproj_weight=self.out_proj.weight,
151
+ outproj_bias=self.out_proj.bias,
152
+ headdim=self.headdim,
153
+ ngroups=self.ngroups,
154
+ norm_before_gate=False,
155
+ initial_states=initial_states,
156
+ **dt_limit_kwargs,
157
+ )
158
+ else:
159
+ z, xBC, dt = torch.split(
160
+ zxbcdt, [self.d_inner, self.d_inner + 2 * self.ngroups * self.d_state, self.nheads], dim=-1
161
+ )
162
+ dt = F.softplus(dt + self.dt_bias) # (B, L, nheads)
163
+ assert self.activation in ["silu", "swish"]
164
+
165
+ # 1D Convolution
166
+ if causal_conv1d_fn is None or self.activation not in ["silu", "swish"]:
167
+ xBC = self.act(
168
+ self.conv1d(xBC.transpose(1, 2)).transpose(1, 2)
169
+ ) # (B, L, self.d_inner + 2 * ngroups * d_state)
170
+ xBC = xBC[:, :seqlen, :]
171
+ else:
172
+ xBC = causal_conv1d_fn(
173
+ x=xBC.transpose(1, 2),
174
+ weight=rearrange(self.conv1d.weight, "d 1 w -> d w"),
175
+ bias=self.conv1d.bias,
176
+ activation=self.activation,
177
+ ).transpose(1, 2)
178
+
179
+ # Split into 3 main branches: X, B, C
180
+ # These correspond to V, K, Q respectively in the SSM/attention duality
181
+ x, B, C = torch.split(xBC, [self.d_inner, self.ngroups * self.d_state, self.ngroups * self.d_state], dim=-1)
182
+ y = mamba_chunk_scan_combined(
183
+ rearrange(x, "b l (h p) -> b l h p", p=self.headdim),
184
+ dt,
185
+ A,
186
+ rearrange(B, "b l (g n) -> b l g n", g=self.ngroups),
187
+ rearrange(C, "b l (g n) -> b l g n", g=self.ngroups),
188
+ chunk_size=self.chunk_size,
189
+ D=self.D,
190
+ z=None,
191
+ seq_idx=seq_idx,
192
+ initial_states=initial_states,
193
+ **dt_limit_kwargs,
194
+ )
195
+ y = rearrange(y, "b l h p -> b l (h p)")
196
+
197
+ # Multiply "gate" branch and apply extra normalization layer
198
+ y = self.norm(y, z)
199
+ out = self.out_proj(y)
200
+ return out
vendor/mamba-og/mamba_ssm/modules/mamba3.py ADDED
@@ -0,0 +1,498 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2026, Dao AI Lab, Goombalab.
2
+
3
+ import math
4
+ from einops import rearrange, repeat
5
+
6
+ import torch
7
+ import torch.nn as nn
8
+ import torch.nn.functional as F
9
+
10
+ from mamba_ssm.ops.triton.layernorm_gated import RMSNorm as RMSNormGated
11
+
12
+ try:
13
+ from mamba_ssm.ops.tilelang.mamba3.mamba3_mimo import mamba3_mimo as mamba3_mimo_combined
14
+ except ImportError:
15
+ mamba3_mimo_combined = None
16
+
17
+ from mamba_ssm.ops.triton.mamba3.mamba3_siso_combined import mamba3_siso_combined
18
+
19
+ from mamba_ssm.ops.triton.mamba3.mamba3_mimo_rotary_step import apply_rotary_qk_inference_fwd
20
+
21
+ try:
22
+ from mamba_ssm.ops.cute.mamba3.mamba3_step_fn import mamba3_step_fn
23
+ except ImportError:
24
+ mamba3_step_fn = None
25
+
26
+ class Mamba3(nn.Module):
27
+ def __init__(
28
+ self,
29
+ d_model,
30
+ d_state=128,
31
+ expand=2,
32
+ headdim=64,
33
+ ngroups=1,
34
+ # ----------------------------------------
35
+ # Mamba-3 configs
36
+ rope_fraction=0.5,
37
+ dt_min=0.001,
38
+ dt_max=0.1,
39
+ dt_init_floor=1e-4,
40
+ A_floor=1e-4,
41
+ is_outproj_norm=False,
42
+ is_mimo=False,
43
+ mimo_rank=4,
44
+ #-------------------------------------------
45
+ # Fused kernel and sharding options
46
+ chunk_size=64, # Recommended: 64 for SISO, 64/mimo_rank for MIMO
47
+ dropout=0.0, # Just to absorb the kwarg
48
+ layer_idx=None, # Absorb kwarg for general module
49
+ n_layer=None, # Absorb kwarg for general module
50
+ device=None,
51
+ dtype=None,
52
+ **kwargs,
53
+ ):
54
+ factory_kwargs = {"device": device, "dtype": dtype}
55
+ super().__init__()
56
+ self.d_model = d_model
57
+ self.d_state = d_state
58
+ self.expand = expand
59
+ self.headdim = headdim
60
+ self.chunk_size = chunk_size
61
+ self.layer_idx = layer_idx
62
+ self.A_floor = A_floor
63
+ self.is_outproj_norm=is_outproj_norm
64
+ self.is_mimo = is_mimo
65
+ self.mimo_rank = mimo_rank
66
+ if not self.is_mimo:
67
+ self.mimo_rank = 1
68
+ else:
69
+ assert mamba3_mimo_combined is not None, "Fails to import Mamba-3 MIMO kernels. Please ensure you installed the necessary dependencies, such as TileLang."
70
+
71
+ self.d_inner = int(self.expand * self.d_model)
72
+ assert self.d_inner % self.headdim == 0
73
+ self.nheads = self.d_inner // self.headdim
74
+ self.num_bc_heads = ngroups
75
+
76
+ # RoPE flags
77
+ assert rope_fraction in [0.5, 1.0]
78
+ self.rotary_dim_divisor = int(2/rope_fraction)
79
+ self.split_tensor_size = int(d_state * rope_fraction)
80
+ if self.split_tensor_size % 2 != 0:
81
+ self.split_tensor_size -= 1
82
+ self.num_rope_angles = self.split_tensor_size // 2
83
+ assert self.num_rope_angles > 0
84
+
85
+ # Order: [z, x, B, C, dd_dt, dd_A, trap, angle]
86
+ d_in_proj = 2 * self.d_inner + 2 * self.d_state * self.num_bc_heads * self.mimo_rank + 3 * self.nheads + self.num_rope_angles
87
+ self.in_proj = nn.Linear(self.d_model, d_in_proj, bias=False, **factory_kwargs)
88
+
89
+ # dt_bias parameterization
90
+ _dt = torch.exp(
91
+ torch.rand(self.nheads, device=device, dtype=torch.float32) * (math.log(dt_max) - math.log(dt_min))
92
+ + math.log(dt_min)
93
+ )
94
+ _dt = torch.clamp(_dt, min=dt_init_floor)
95
+ _dt_bias = _dt + torch.log(-torch.expm1(-_dt))
96
+ self.dt_bias = nn.Parameter(_dt_bias, requires_grad=True)
97
+ self.dt_bias._no_weight_decay = True
98
+
99
+ # B and C biases
100
+ self.B_bias = nn.Parameter(1+torch.zeros((self.nheads, self.mimo_rank, self.d_state), dtype=torch.float32, device=device), requires_grad=True)
101
+ self.C_bias = nn.Parameter(1+torch.zeros((self.nheads, self.mimo_rank, self.d_state), dtype=torch.float32, device=device), requires_grad=True)
102
+
103
+ # RMS Norm for B and C
104
+ assert RMSNormGated is not None
105
+ self.B_norm = RMSNormGated(self.d_state, eps=1e-5, **factory_kwargs)
106
+ self.C_norm = RMSNormGated(self.d_state, eps=1e-5, **factory_kwargs)
107
+
108
+ if self.is_mimo:
109
+ # Initialize up/down MIMO projection (for x and z)
110
+ mimo_x_init_weights = torch.ones(self.nheads, self.mimo_rank, self.headdim, device=device) / self.mimo_rank
111
+ mimo_z_init_weights = torch.ones(self.nheads, self.mimo_rank, self.headdim, device=device)
112
+ mimo_o_init_weights = torch.ones(self.nheads, self.mimo_rank, self.headdim, device=device) / self.mimo_rank
113
+
114
+ self.mimo_x = nn.Parameter(mimo_x_init_weights, requires_grad=True)
115
+ self.mimo_z = nn.Parameter(mimo_z_init_weights, requires_grad=True)
116
+ self.mimo_o = nn.Parameter(mimo_o_init_weights, requires_grad=True)
117
+
118
+ # D "skip" parameter
119
+ self.D = nn.Parameter(torch.ones(self.nheads, device=device))
120
+ self.D._no_weight_decay = True
121
+
122
+ if self.is_outproj_norm:
123
+ self.norm = RMSNormGated(
124
+ self.d_inner,
125
+ eps=1e-5,
126
+ norm_before_gate=True,
127
+ group_size=self.headdim,
128
+ **factory_kwargs
129
+ )
130
+
131
+ # Output projection
132
+ self.out_proj = nn.Linear(self.d_inner, self.d_model, bias=False, **factory_kwargs)
133
+
134
+
135
+ def forward(self, u, seq_idx=None, cu_seqlens=None, inference_params=None):
136
+ """
137
+ u: (batch, seqlen, hidden_dim)
138
+ Returns: same shape as u
139
+ """
140
+ batch, seqlen, dim = u.shape
141
+
142
+ angle_dt_state, ssm_state, k_state, v_state = None, None, None, None
143
+ if inference_params is not None:
144
+ inference_batch = cu_seqlens.shape[0] - 1 if cu_seqlens is not None else batch
145
+ angle_dt_state, ssm_state, k_state, v_state = self._get_states_from_cache(inference_params, inference_batch)
146
+ if inference_params.seqlen_offset > 0:
147
+ out, _, _, _, _ = self.step(u, angle_dt_state, ssm_state, k_state, v_state)
148
+ return out
149
+
150
+ # Apply in_proj
151
+ zxBCdtAtrap = self.in_proj(u)
152
+ z, x, B, C, dd_dt, dd_A, trap, angles = torch.split(
153
+ zxBCdtAtrap,
154
+ [
155
+ self.d_inner, self.d_inner,
156
+ self.d_state * self.num_bc_heads * self.mimo_rank,
157
+ self.d_state * self.num_bc_heads * self.mimo_rank,
158
+ self.nheads, self.nheads, self.nheads,
159
+ self.num_rope_angles
160
+ ],
161
+ dim=-1)
162
+ z = rearrange(z, "b l (h p) -> b l h p", p=self.headdim)
163
+ x = rearrange(x, "b l (h p) -> b l h p", p=self.headdim)
164
+ B = rearrange(B, "b l (r g n) -> b l r g n", r=self.mimo_rank, g=self.num_bc_heads)
165
+ C = rearrange(C, "b l (r g n) -> b l r g n", r=self.mimo_rank, g=self.num_bc_heads)
166
+ trap = rearrange(trap, "b l h -> b h l")
167
+
168
+ # Compute ADT, DT
169
+ _A = -F.softplus(dd_A.to(torch.float32)) # (B, L, N)
170
+ _A = torch.clamp(_A, max=-self.A_floor)
171
+ DT = F.softplus(dd_dt + self.dt_bias) # (B, L, N)
172
+ ADT = _A * DT
173
+ DT = rearrange(DT, "b l n -> b n l")
174
+ ADT = rearrange(ADT, "b l n -> b n l")
175
+
176
+ # Compute angle — cast to float32 as required by the MIMO/SISO kernels
177
+ angles = angles.unsqueeze(-2).expand(-1, -1, self.nheads, -1).to(torch.float32) # (B, L, N, S)
178
+
179
+ # Apply RMS Norm on B and C
180
+ B = self.B_norm(B)
181
+ C = self.C_norm(C)
182
+
183
+ # Apply Mamba-3 kernel
184
+ if self.is_mimo:
185
+ y = mamba3_mimo_combined(
186
+ Q=C,
187
+ K=B,
188
+ V=x,
189
+ ADT=ADT,
190
+ DT=DT,
191
+ Trap=trap,
192
+ Q_bias=self.C_bias,
193
+ K_bias=self.B_bias,
194
+ MIMO_V=self.mimo_x,
195
+ MIMO_Z=self.mimo_z,
196
+ MIMO_Out=self.mimo_o if not self.is_outproj_norm else None,
197
+ Angles=angles,
198
+ D=self.D,
199
+ Z=z if not self.is_outproj_norm else None,
200
+ chunk_size=self.chunk_size,
201
+ rotary_dim_divisor=self.rotary_dim_divisor,
202
+ dtype=x.dtype,
203
+ return_state=ssm_state is not None,
204
+ cu_seqlens=cu_seqlens,
205
+ )
206
+ if ssm_state is not None:
207
+ y, last_angle, last_state, last_k, last_v, *rest = y
208
+ angle_dt_state.copy_(last_angle)
209
+ ssm_state.copy_(last_state)
210
+ k_state.copy_(last_k)
211
+ v_state.copy_(last_v)
212
+ if self.is_outproj_norm:
213
+ z = torch.einsum("blhp,hrp->blrhp", z.float(), self.mimo_z)
214
+ z = rearrange(z, "b l r h p -> b l r (h p)")
215
+ y = rearrange(y, "b l r h p -> b l r (h p)").float()
216
+ y = self.norm(y, z)
217
+ y = rearrange(y, "b l r (h p) -> b l r h p", p=self.headdim)
218
+ y = torch.einsum("blrhp,hrp->blhp", y, self.mimo_o)
219
+ y = rearrange(y, "b l h p -> b l (h p)")
220
+ else:
221
+ y = mamba3_siso_combined(
222
+ Q=C.squeeze(2),
223
+ K=B.squeeze(2),
224
+ V=x,
225
+ ADT=ADT,
226
+ DT=DT,
227
+ Trap=trap,
228
+ Q_bias=self.C_bias.squeeze(1),
229
+ K_bias=self.B_bias.squeeze(1),
230
+ Angles=angles,
231
+ D=self.D,
232
+ Z=z if not self.is_outproj_norm else None,
233
+ chunk_size=self.chunk_size,
234
+ Input_States=None,
235
+ return_final_states=ssm_state is not None,
236
+ cu_seqlens=cu_seqlens,
237
+ )
238
+ if ssm_state is not None:
239
+ y, last_angle, last_state, last_k, last_v, *rest = y
240
+ angle_dt_state.copy_(last_angle)
241
+ ssm_state.copy_(last_state)
242
+ k_state.copy_(last_k.unsqueeze(1))
243
+ v_state.copy_(last_v)
244
+ y = rearrange(y, "b l h p -> b l (h p)")
245
+ if self.is_outproj_norm:
246
+ z = rearrange(z, "b l h p -> b l (h p)")
247
+ y = self.norm(y, z)
248
+
249
+ out = self.out_proj(y.to(x.dtype))
250
+ return out
251
+
252
+
253
+ def _preprocess(self, A_proj, dd_dt, B, C, x, z, trap_proj, angle_proj):
254
+ _A = -F.softplus(A_proj.to(torch.float32))
255
+ _A = torch.clamp(_A, max=-self.A_floor)
256
+ DT = F.softplus(dd_dt + self.dt_bias)
257
+ trap = torch.sigmoid(trap_proj)
258
+
259
+ rank = self.mimo_rank if self.is_mimo else 1
260
+ B = rearrange(B, "b (r g s) -> b r g s", g=self.num_bc_heads, r=rank)
261
+ C = rearrange(C, "b (r g s) -> b r g s", g=self.num_bc_heads, r=rank)
262
+
263
+ B = self.B_norm(B)
264
+ C = self.C_norm(C)
265
+
266
+ B = B.expand(-1, -1, self.nheads, -1) # (B, R, N, S)
267
+ C = C.expand(-1, -1, self.nheads, -1) # (B, R, N, S)
268
+
269
+ x = rearrange(x, "b (h p) -> b h p", p=self.headdim)
270
+ z = rearrange(z, "b (h p) -> b h p", p=self.headdim)
271
+
272
+ angles = angle_proj.unsqueeze(-2).expand(-1, self.nheads, -1)
273
+
274
+ return DT, B, C, x, z, trap, _A, angles
275
+
276
+ def _postprocess(self, y, outpj, z, zpj, headdim):
277
+ # y: (batch, R, H, D) — apply mimo_z to z, then norm, then mimo_o
278
+ z_r = torch.einsum("bhp,rhp->brhp", z.float(), zpj) # (batch, R, H, D)
279
+ z_r = rearrange(z_r, "b r h p -> b r (h p)")
280
+ y = rearrange(y, "b r h p -> b r (h p)").float()
281
+ y = self.norm(y, z_r)
282
+ y = rearrange(y, "b r (h p) -> b r h p", p=headdim)
283
+ y = torch.einsum("brhp,rhp->bhp", y, outpj) # (batch, H, D)
284
+ return y
285
+
286
+ def step(self, u, angle_state, ssm_state, k_state, v_state, **kwargs):
287
+ """
288
+ Decode function using CuteDSL kernel from mamba3_step_fn.py.
289
+ Also modify the state vars in-place for the next step.
290
+
291
+ NOTE: Only tested on H100. Compatibility with other hardware
292
+ will be made available in the future.
293
+
294
+ Args:
295
+ u: (batch, d_model)
296
+ angle_state: (batch, nheads, num_rope_angles)
297
+ ssm_state: (batch, nheads, headdim, d_state)
298
+ k_state: (batch, R, nheads, d_state), where R = mimo_rank (R=1 if not MIMO)
299
+ v_state: (batch, nheads, headdim)
300
+ **kwargs: ignored
301
+ Returns:
302
+ out: (batch, d_model)
303
+ nxt_angle_state: (batch, nheads, num_rope_angles)
304
+ state_out: (batch, nheads, headdim, d_state)
305
+ nxt_k_state: (batch, R, nheads, d_state), where R = mimo_rank (R=1 if not MIMO)
306
+ nxt_v_state: (batch, nheads, headdim)
307
+ """
308
+ assert mamba3_step_fn is not None, "Cute Mamba-3 step function is not available. Please ensure you installed the necessary dependencies, such as nvidia-cutlass-dsl and quack-kernels."
309
+
310
+ # in_proj
311
+ zxBCdt = self.in_proj(u)
312
+ z, x, B, C, dd_dt, dd_A, trap, angles = torch.split(
313
+ zxBCdt,
314
+ [
315
+ self.d_inner,
316
+ self.d_inner,
317
+ self.d_state * self.num_bc_heads * self.mimo_rank,
318
+ self.d_state * self.num_bc_heads * self.mimo_rank,
319
+ self.nheads,
320
+ self.nheads,
321
+ self.nheads,
322
+ self.num_rope_angles,
323
+ ],
324
+ dim=-1)
325
+
326
+ DT, B, C, x, z, trap, A, angles = self._preprocess(
327
+ dd_A, dd_dt, B, C, x, z, trap, angles)
328
+
329
+ bias_q = rearrange(self.C_bias, "h r n -> r h n")
330
+ bias_k = rearrange(self.B_bias, "h r n -> r h n")
331
+
332
+ # NOTE: MIMO calls the Tilelang kernel,
333
+ # which permute the blockwise rotation matrix so that
334
+ # the i-th entry is paired with the i+N//2-th entry:
335
+ rotate_pairwise = not self.is_mimo
336
+ C, B, nxt_angle_state = apply_rotary_qk_inference_fwd(
337
+ q=C, k=B, angle_state=angle_state,
338
+ angle_proj=angles, dt=DT, bias_q=bias_q, bias_k=bias_k,
339
+ conjugate=False, inplace=False, # NOTE: inplace is incompatible with self.nheads != self.num_bc_heads
340
+ rotate_pairwise=rotate_pairwise)
341
+
342
+ nxt_v_state = x
343
+ nxt_k_state = B
344
+
345
+ if self.is_mimo:
346
+ xpj = rearrange(self.mimo_x, "h r p -> r h p", p=self.headdim).contiguous()
347
+ zpj = rearrange(self.mimo_z, "h r p -> r h p", p=self.headdim).contiguous()
348
+ outpj = rearrange(self.mimo_o, "h r p -> r h p", p=self.headdim).contiguous()
349
+ else:
350
+ xpj = torch.ones(self.mimo_rank, self.nheads, self.headdim, device=x.device, dtype=x.dtype)
351
+ zpj = torch.ones(self.mimo_rank, self.nheads, self.headdim, device=z.device, dtype=z.dtype)
352
+ outpj = torch.ones(self.mimo_rank, self.nheads, self.headdim, device=x.device, dtype=x.dtype)
353
+
354
+ if self.is_outproj_norm:
355
+ batch = x.shape[0]
356
+ y = torch.empty(batch, self.mimo_rank, self.nheads, self.headdim, device=x.device, dtype=x.dtype)
357
+ mamba3_step_fn(
358
+ ssm_state,
359
+ k_state,
360
+ v_state,
361
+ A,
362
+ B,
363
+ C,
364
+ self.D,
365
+ x,
366
+ DT,
367
+ trap,
368
+ xpj,
369
+ outproj=None,
370
+ state_out=None, # can be not in place if pass in state_out
371
+ out=y,
372
+ z=None,
373
+ zproj=None,
374
+ tile_D=64,
375
+ num_warps=4,
376
+ )
377
+ y = self._postprocess(y, outpj, z, zpj, self.headdim)
378
+ else:
379
+ y = torch.empty_like(x)
380
+ mamba3_step_fn(
381
+ ssm_state,
382
+ k_state,
383
+ v_state,
384
+ A,
385
+ B,
386
+ C,
387
+ self.D,
388
+ x,
389
+ DT,
390
+ trap,
391
+ xpj,
392
+ outproj=outpj,
393
+ state_out=None, # can be not in place if pass in state_out
394
+ out=y,
395
+ z=z,
396
+ zproj=zpj,
397
+ tile_D=64,
398
+ num_warps=4,
399
+ )
400
+
401
+ # out_proj
402
+ out = rearrange(y, "b h p -> b (h p)")
403
+ out = self.out_proj(out.to(x.dtype))
404
+
405
+ angle_state.copy_(nxt_angle_state)
406
+ # Uncomment the following if mamba3_step_fn is not in place:
407
+ # state_out = torch.empty_like(ssm_state)
408
+ # ssm_state.copy_(state_out)
409
+ k_state.copy_(nxt_k_state)
410
+ v_state.copy_(nxt_v_state)
411
+
412
+ return out, nxt_angle_state, ssm_state, nxt_k_state, nxt_v_state
413
+
414
+ def allocate_inference_cache(self, batch_size, max_seqlen, device=None, dtype=None, inplace_state=None, **kwargs):
415
+ device = self.in_proj.weight.device if device is None else device
416
+ dtype = self.in_proj.weight.dtype if dtype is None else dtype
417
+
418
+ # RoPE State
419
+ angle_dt_state = torch.zeros(
420
+ (batch_size, self.nheads, self.num_rope_angles),
421
+ device=device,
422
+ dtype=torch.float32,
423
+ )
424
+
425
+ # Mamba-3 Combined Kernel States
426
+ # SSM State
427
+ ssm_state = torch.zeros(
428
+ (batch_size, self.nheads, self.headdim, self.d_state),
429
+ device=device,
430
+ dtype=torch.float32,
431
+ )
432
+
433
+ # K (=B) State
434
+ if self.is_mimo:
435
+ k_state = torch.zeros(
436
+ (batch_size, self.mimo_rank, self.nheads, self.d_state),
437
+ device=device,
438
+ dtype=dtype,
439
+ )
440
+ else:
441
+ k_state = torch.zeros(
442
+ (batch_size, 1, self.nheads, self.d_state),
443
+ device=device,
444
+ dtype=dtype,
445
+ )
446
+
447
+ # V (=x) State
448
+ v_state = torch.zeros(
449
+ (batch_size, self.nheads, self.headdim),
450
+ device=device,
451
+ dtype=dtype,
452
+ )
453
+
454
+ return (angle_dt_state, ssm_state, k_state, v_state)
455
+
456
+ def _get_states_from_cache(self, inference_params, batch_size, initialize_states=False):
457
+ assert self.layer_idx is not None
458
+ device = self.in_proj.weight.device
459
+ dtype = self.in_proj.weight.dtype
460
+
461
+ if self.layer_idx not in inference_params.key_value_memory_dict:
462
+ angle_dt_state = torch.zeros(
463
+ (batch_size, self.nheads, self.num_rope_angles),
464
+ device=device,
465
+ dtype=torch.float32,
466
+ )
467
+ ssm_state = torch.zeros(
468
+ (batch_size, self.nheads, self.headdim, self.d_state),
469
+ device=device,
470
+ dtype=torch.float32,
471
+ )
472
+ if self.is_mimo:
473
+ k_state = torch.zeros(
474
+ (batch_size, self.mimo_rank, self.nheads, self.d_state),
475
+ device=device,
476
+ dtype=dtype,
477
+ )
478
+ else:
479
+ k_state = torch.zeros(
480
+ (batch_size, 1, self.nheads, self.d_state),
481
+ device=device,
482
+ dtype=dtype,
483
+ )
484
+ v_state = torch.zeros(
485
+ (batch_size, self.nheads, self.headdim),
486
+ device=device,
487
+ dtype=dtype,
488
+ )
489
+ inference_params.key_value_memory_dict[self.layer_idx] = (angle_dt_state, ssm_state, k_state, v_state)
490
+ else:
491
+ angle_dt_state, ssm_state, k_state, v_state = inference_params.key_value_memory_dict[self.layer_idx]
492
+ # TODO: What if batch size changes between generation, and we reuse the same states?
493
+ if initialize_states:
494
+ angle_dt_state.zero_()
495
+ ssm_state.zero_()
496
+ k_state.zero_()
497
+ v_state.zero_()
498
+ return angle_dt_state, ssm_state, k_state, v_state
vendor/mamba-og/mamba_ssm/modules/mamba_simple.py ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023, Tri Dao, Albert Gu.
2
+
3
+ import math
4
+ from typing import Optional
5
+
6
+ import torch
7
+ import torch.nn as nn
8
+ import torch.nn.functional as F
9
+ from torch import Tensor
10
+
11
+ from einops import rearrange, repeat
12
+
13
+ from mamba_ssm.ops.selective_scan_interface import selective_scan_fn, mamba_inner_fn
14
+
15
+ try:
16
+ from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
17
+ except ImportError:
18
+ causal_conv1d_fn, causal_conv1d_update = None, None
19
+
20
+ try:
21
+ from mamba_ssm.ops.triton.selective_state_update import selective_state_update
22
+ except ImportError:
23
+ selective_state_update = None
24
+
25
+ try:
26
+ from mamba_ssm.ops.triton.layer_norm import RMSNorm, layer_norm_fn, rms_norm_fn
27
+ except ImportError:
28
+ RMSNorm, layer_norm_fn, rms_norm_fn = None, None, None
29
+
30
+
31
+ class Mamba(nn.Module):
32
+ def __init__(
33
+ self,
34
+ d_model,
35
+ d_state=16,
36
+ d_conv=4,
37
+ expand=2,
38
+ dt_rank="auto",
39
+ dt_min=0.001,
40
+ dt_max=0.1,
41
+ dt_init="random",
42
+ dt_scale=1.0,
43
+ dt_init_floor=1e-4,
44
+ conv_bias=True,
45
+ bias=False,
46
+ use_fast_path=True, # Fused kernel options
47
+ layer_idx=None,
48
+ device=None,
49
+ dtype=None,
50
+ ):
51
+ factory_kwargs = {"device": device, "dtype": dtype}
52
+ super().__init__()
53
+ self.d_model = d_model
54
+ self.d_state = d_state
55
+ self.d_conv = d_conv
56
+ self.expand = expand
57
+ self.d_inner = int(self.expand * self.d_model)
58
+ self.dt_rank = math.ceil(self.d_model / 16) if dt_rank == "auto" else dt_rank
59
+ self.use_fast_path = use_fast_path
60
+ self.layer_idx = layer_idx
61
+
62
+ self.in_proj = nn.Linear(self.d_model, self.d_inner * 2, bias=bias, **factory_kwargs)
63
+
64
+ self.conv1d = nn.Conv1d(
65
+ in_channels=self.d_inner,
66
+ out_channels=self.d_inner,
67
+ bias=conv_bias,
68
+ kernel_size=d_conv,
69
+ groups=self.d_inner,
70
+ padding=d_conv - 1,
71
+ **factory_kwargs,
72
+ )
73
+
74
+ self.activation = "silu"
75
+ self.act = nn.SiLU()
76
+
77
+ self.x_proj = nn.Linear(
78
+ self.d_inner, self.dt_rank + self.d_state * 2, bias=False, **factory_kwargs
79
+ )
80
+ self.dt_proj = nn.Linear(self.dt_rank, self.d_inner, bias=True, **factory_kwargs)
81
+
82
+ # Initialize special dt projection to preserve variance at initialization
83
+ dt_init_std = self.dt_rank**-0.5 * dt_scale
84
+ if dt_init == "constant":
85
+ nn.init.constant_(self.dt_proj.weight, dt_init_std)
86
+ elif dt_init == "random":
87
+ nn.init.uniform_(self.dt_proj.weight, -dt_init_std, dt_init_std)
88
+ else:
89
+ raise NotImplementedError
90
+
91
+ # Initialize dt bias so that F.softplus(dt_bias) is between dt_min and dt_max
92
+ dt = torch.exp(
93
+ torch.rand(self.d_inner, **factory_kwargs) * (math.log(dt_max) - math.log(dt_min))
94
+ + math.log(dt_min)
95
+ ).clamp(min=dt_init_floor)
96
+ # Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759
97
+ inv_dt = dt + torch.log(-torch.expm1(-dt))
98
+ with torch.no_grad():
99
+ self.dt_proj.bias.copy_(inv_dt)
100
+ # Our initialization would set all Linear.bias to zero, need to mark this one as _no_reinit
101
+ self.dt_proj.bias._no_reinit = True
102
+
103
+ # S4D real initialization
104
+ A = repeat(
105
+ torch.arange(1, self.d_state + 1, dtype=torch.float32, device=device),
106
+ "n -> d n",
107
+ d=self.d_inner,
108
+ ).contiguous()
109
+ A_log = torch.log(A) # Keep A_log in fp32
110
+ self.A_log = nn.Parameter(A_log)
111
+ self.A_log._no_weight_decay = True
112
+
113
+ # D "skip" parameter
114
+ self.D = nn.Parameter(torch.ones(self.d_inner, device=device)) # Keep in fp32
115
+ self.D._no_weight_decay = True
116
+
117
+ self.out_proj = nn.Linear(self.d_inner, self.d_model, bias=bias, **factory_kwargs)
118
+
119
+ def forward(self, hidden_states, inference_params=None):
120
+ """
121
+ hidden_states: (B, L, D)
122
+ Returns: same shape as hidden_states
123
+ """
124
+ batch, seqlen, dim = hidden_states.shape
125
+
126
+ conv_state, ssm_state = None, None
127
+ if inference_params is not None:
128
+ conv_state, ssm_state = self._get_states_from_cache(inference_params, batch)
129
+ if inference_params.seqlen_offset > 0:
130
+ # The states are updated inplace
131
+ out, _, _ = self.step(hidden_states, conv_state, ssm_state)
132
+ return out
133
+
134
+ # We do matmul and transpose BLH -> HBL at the same time
135
+ xz = rearrange(
136
+ self.in_proj.weight @ rearrange(hidden_states, "b l d -> d (b l)"),
137
+ "d (b l) -> b d l",
138
+ l=seqlen,
139
+ )
140
+ if self.in_proj.bias is not None:
141
+ xz = xz + rearrange(self.in_proj.bias.to(dtype=xz.dtype), "d -> d 1")
142
+
143
+ A = -torch.exp(self.A_log.float()) # (d_inner, d_state)
144
+ # In the backward pass we write dx and dz next to each other to avoid torch.cat
145
+ if self.use_fast_path and causal_conv1d_fn is not None and inference_params is None: # Doesn't support outputting the states
146
+ out = mamba_inner_fn(
147
+ xz,
148
+ self.conv1d.weight,
149
+ self.conv1d.bias,
150
+ self.x_proj.weight,
151
+ self.dt_proj.weight,
152
+ self.out_proj.weight,
153
+ self.out_proj.bias,
154
+ A,
155
+ None, # input-dependent B
156
+ None, # input-dependent C
157
+ self.D.float(),
158
+ delta_bias=self.dt_proj.bias.float(),
159
+ delta_softplus=True,
160
+ )
161
+ else:
162
+ x, z = xz.chunk(2, dim=1)
163
+ # Compute short convolution
164
+ if conv_state is not None:
165
+ # If we just take x[:, :, -self.d_conv :], it will error if seqlen < self.d_conv
166
+ # Instead F.pad will pad with zeros if seqlen < self.d_conv, and truncate otherwise.
167
+ conv_state.copy_(F.pad(x, (self.d_conv - x.shape[-1], 0))) # Update state (B D W)
168
+ if causal_conv1d_fn is None:
169
+ x = self.act(self.conv1d(x)[..., :seqlen])
170
+ else:
171
+ assert self.activation in ["silu", "swish"]
172
+ x = causal_conv1d_fn(
173
+ x=x,
174
+ weight=rearrange(self.conv1d.weight, "d 1 w -> d w"),
175
+ bias=self.conv1d.bias,
176
+ activation=self.activation,
177
+ )
178
+
179
+ # We're careful here about the layout, to avoid extra transposes.
180
+ # We want dt to have d as the slowest moving dimension
181
+ # and L as the fastest moving dimension, since those are what the ssm_scan kernel expects.
182
+ x_dbl = self.x_proj(rearrange(x, "b d l -> (b l) d")) # (bl d)
183
+ dt, B, C = torch.split(x_dbl, [self.dt_rank, self.d_state, self.d_state], dim=-1)
184
+ dt = self.dt_proj.weight @ dt.t()
185
+ dt = rearrange(dt, "d (b l) -> b d l", l=seqlen)
186
+ B = rearrange(B, "(b l) dstate -> b dstate l", l=seqlen).contiguous()
187
+ C = rearrange(C, "(b l) dstate -> b dstate l", l=seqlen).contiguous()
188
+ assert self.activation in ["silu", "swish"]
189
+ y = selective_scan_fn(
190
+ x,
191
+ dt,
192
+ A,
193
+ B,
194
+ C,
195
+ self.D.float(),
196
+ z=z,
197
+ delta_bias=self.dt_proj.bias.float(),
198
+ delta_softplus=True,
199
+ return_last_state=ssm_state is not None,
200
+ )
201
+ if ssm_state is not None:
202
+ y, last_state = y
203
+ ssm_state.copy_(last_state)
204
+ y = rearrange(y, "b d l -> b l d")
205
+ out = self.out_proj(y)
206
+ return out
207
+
208
+ def step(self, hidden_states, conv_state, ssm_state):
209
+ dtype = hidden_states.dtype
210
+ assert hidden_states.shape[1] == 1, "Only support decoding with 1 token at a time for now"
211
+ xz = self.in_proj(hidden_states.squeeze(1)) # (B 2D)
212
+ x, z = xz.chunk(2, dim=-1) # (B D)
213
+
214
+ # Conv step
215
+ if causal_conv1d_update is None:
216
+ conv_state.copy_(torch.roll(conv_state, shifts=-1, dims=-1)) # Update state (B D W)
217
+ conv_state[:, :, -1] = x
218
+ x = torch.sum(conv_state * rearrange(self.conv1d.weight, "d 1 w -> d w"), dim=-1) # (B D)
219
+ if self.conv1d.bias is not None:
220
+ x = x + self.conv1d.bias
221
+ x = self.act(x).to(dtype=dtype)
222
+ else:
223
+ x = causal_conv1d_update(
224
+ x,
225
+ conv_state,
226
+ rearrange(self.conv1d.weight, "d 1 w -> d w"),
227
+ self.conv1d.bias,
228
+ self.activation,
229
+ )
230
+
231
+ x_db = self.x_proj(x) # (B dt_rank+2*d_state)
232
+ dt, B, C = torch.split(x_db, [self.dt_rank, self.d_state, self.d_state], dim=-1)
233
+ # Don't add dt_bias here
234
+ dt = F.linear(dt, self.dt_proj.weight) # (B d_inner)
235
+ A = -torch.exp(self.A_log.float()) # (d_inner, d_state)
236
+
237
+ # SSM step
238
+ if selective_state_update is None:
239
+ # Discretize A and B
240
+ dt = F.softplus(dt + self.dt_proj.bias.to(dtype=dt.dtype))
241
+ dA = torch.exp(torch.einsum("bd,dn->bdn", dt, A))
242
+ dB = torch.einsum("bd,bn->bdn", dt, B)
243
+ ssm_state.copy_(ssm_state * dA + rearrange(x, "b d -> b d 1") * dB)
244
+ y = torch.einsum("bdn,bn->bd", ssm_state.to(dtype), C)
245
+ y = y + self.D.to(dtype) * x
246
+ y = y * self.act(z) # (B D)
247
+ else:
248
+ y = selective_state_update(
249
+ ssm_state, x, dt, A, B, C, self.D, z=z, dt_bias=self.dt_proj.bias, dt_softplus=True
250
+ )
251
+
252
+ out = self.out_proj(y)
253
+ return out.unsqueeze(1), conv_state, ssm_state
254
+
255
+ def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None, **kwargs):
256
+ device = self.out_proj.weight.device
257
+ conv_dtype = self.conv1d.weight.dtype if dtype is None else dtype
258
+ conv_state = torch.zeros(
259
+ batch_size, self.d_model * self.expand, self.d_conv, device=device, dtype=conv_dtype
260
+ )
261
+ ssm_dtype = self.dt_proj.weight.dtype if dtype is None else dtype
262
+ # ssm_dtype = torch.float32
263
+ ssm_state = torch.zeros(
264
+ batch_size, self.d_model * self.expand, self.d_state, device=device, dtype=ssm_dtype
265
+ )
266
+ return conv_state, ssm_state
267
+
268
+ def _get_states_from_cache(self, inference_params, batch_size, initialize_states=False):
269
+ assert self.layer_idx is not None
270
+ if self.layer_idx not in inference_params.key_value_memory_dict:
271
+ batch_shape = (batch_size,)
272
+ conv_state = torch.zeros(
273
+ batch_size,
274
+ self.d_model * self.expand,
275
+ self.d_conv,
276
+ device=self.conv1d.weight.device,
277
+ dtype=self.conv1d.weight.dtype,
278
+ )
279
+ ssm_state = torch.zeros(
280
+ batch_size,
281
+ self.d_model * self.expand,
282
+ self.d_state,
283
+ device=self.dt_proj.weight.device,
284
+ dtype=self.dt_proj.weight.dtype,
285
+ # dtype=torch.float32,
286
+ )
287
+ inference_params.key_value_memory_dict[self.layer_idx] = (conv_state, ssm_state)
288
+ else:
289
+ conv_state, ssm_state = inference_params.key_value_memory_dict[self.layer_idx]
290
+ # TODO: What if batch size changes between generation, and we reuse the same states?
291
+ if initialize_states:
292
+ conv_state.zero_()
293
+ ssm_state.zero_()
294
+ return conv_state, ssm_state
vendor/mamba-og/mamba_ssm/modules/mha.py ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024, Tri Dao, Albert Gu.
2
+
3
+ import math
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.nn.functional as F
8
+ from einops import rearrange
9
+
10
+ try:
11
+ from flash_attn import flash_attn_with_kvcache
12
+ except ImportError:
13
+ flash_attn_with_kvcache = None
14
+
15
+ try:
16
+ from flash_attn.layers.rotary import RotaryEmbedding
17
+ except ImportError:
18
+ RotaryEmbedding = None
19
+
20
+ try:
21
+ from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
22
+ except ImportError:
23
+ causal_conv1d_fn, causal_conv1d_update = None, None
24
+
25
+
26
+ def _update_kv_cache(kv, inference_params, layer_idx):
27
+ """kv: (batch_size, seqlen, 2, nheads, head_dim) or (batch_size, 1, 2, nheads, head_dim)"""
28
+ # Pre-allocate memory for key-values for inference.
29
+ num_heads, head_dim = kv.shape[-2:]
30
+ assert layer_idx in inference_params.key_value_memory_dict
31
+ kv_cache, _ = inference_params.key_value_memory_dict[layer_idx]
32
+ # Adjust key and value for inference
33
+ batch_start = inference_params.batch_size_offset
34
+ batch_end = batch_start + kv.shape[0]
35
+ sequence_start = inference_params.seqlen_offset
36
+ sequence_end = sequence_start + kv.shape[1]
37
+ assert batch_end <= kv_cache.shape[0]
38
+ assert sequence_end <= kv_cache.shape[1]
39
+ assert kv_cache is not None
40
+ kv_cache[batch_start:batch_end, sequence_start:sequence_end, ...] = kv
41
+ return kv_cache[batch_start:batch_end, :sequence_end, ...]
42
+
43
+
44
+ class MHA(nn.Module):
45
+ """Multi-head self-attention and cross-attention"""
46
+
47
+ def __init__(
48
+ self,
49
+ embed_dim,
50
+ num_heads,
51
+ num_heads_kv=None,
52
+ head_dim=None, # If None, use embed_dim // num_heads
53
+ mlp_dim=0,
54
+ qkv_proj_bias=True,
55
+ out_proj_bias=True,
56
+ softmax_scale=None,
57
+ causal=False,
58
+ layer_idx=None,
59
+ d_conv=0,
60
+ rotary_emb_dim=0,
61
+ rotary_emb_base=10000.0,
62
+ rotary_emb_interleaved=False,
63
+ device=None,
64
+ dtype=None,
65
+ ) -> None:
66
+ """
67
+ num_heads_kv: can be used to toggle MQA / GQA. If None, use num_heads.
68
+ return_residual: whether to return the input x along with the output. This is for
69
+ performance reason: for post-norm architecture, returning the input allows us
70
+ to fuse the backward of nn.Linear with the residual connection.
71
+ """
72
+ factory_kwargs = {"device": device, "dtype": dtype}
73
+ super().__init__()
74
+ self.embed_dim = embed_dim
75
+ self.layer_idx = layer_idx
76
+ self.d_conv = d_conv
77
+ self.rotary_emb_dim = rotary_emb_dim
78
+ self.softmax_scale = softmax_scale
79
+ self.causal = causal
80
+
81
+ self.num_heads = num_heads
82
+ self.num_heads_kv = num_heads_kv if num_heads_kv is not None else num_heads
83
+ assert (
84
+ self.num_heads % self.num_heads_kv == 0
85
+ ), "num_heads must be divisible by num_heads_kv"
86
+ if head_dim is None:
87
+ assert self.embed_dim % num_heads == 0, "embed_dim must be divisible by num_heads"
88
+ self.head_dim = head_dim if head_dim is not None else self.embed_dim // num_heads
89
+ self.mlp_dim = math.ceil(mlp_dim / 256) * 256
90
+ qkv_dim = self.head_dim * (self.num_heads + 2 * self.num_heads_kv)
91
+ out_dim = self.head_dim * self.num_heads
92
+
93
+ if self.rotary_emb_dim > 0:
94
+ assert RotaryEmbedding is not None, "rotary requires flash_attn to be installed"
95
+ self.rotary_emb = RotaryEmbedding(
96
+ self.rotary_emb_dim,
97
+ base=rotary_emb_base,
98
+ interleaved=rotary_emb_interleaved,
99
+ device=device,
100
+ )
101
+
102
+ self.in_proj = nn.Linear(embed_dim, qkv_dim + self.mlp_dim, bias=qkv_proj_bias, **factory_kwargs)
103
+ if self.d_conv > 0:
104
+ self.conv1d = nn.Conv1d(
105
+ qkv_dim, qkv_dim, kernel_size=self.d_conv, padding=self.d_conv - 1, groups=qkv_dim,
106
+ **factory_kwargs
107
+ )
108
+ self.out_proj = nn.Linear(out_dim + self.mlp_dim // 2, embed_dim, bias=out_proj_bias, **factory_kwargs)
109
+
110
+ def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None):
111
+ dtype = self.out_proj.weight.dtype if dtype is None else dtype
112
+ device = self.out_proj.weight.device
113
+ if self.d_conv > 0:
114
+ conv_state = torch.zeros(
115
+ batch_size, self.conv1d.weight.shape[0], self.d_conv, device=device, dtype=dtype
116
+ )
117
+ else:
118
+ conv_state = None
119
+ kv_cache = torch.empty(
120
+ batch_size, max_seqlen, 2, self.num_heads_kv, self.head_dim, dtype=dtype, device=device,
121
+ )
122
+ return kv_cache, conv_state
123
+
124
+ def _update_kv_cache(self, kv, inference_params):
125
+ """kv: (batch_size, seqlen, 2, nheads, head_dim) or (batch_size, 1, 2, nheads, head_dim)"""
126
+ assert self.layer_idx is not None, "Generation requires layer_idx in the constructor"
127
+ return _update_kv_cache(kv, inference_params, self.layer_idx)
128
+
129
+ def _apply_rotary_update_kvcache_attention(self, q, kv, inference_params):
130
+ """
131
+ Fast path that combine 3 steps: apply rotary to Q and K, update kv cache, and apply attention.
132
+ q: (batch_size, seqlen_q, nheads, head_dim)
133
+ kv: (batch_size, seqlen_k, 2, nheads_kv, head_dim)
134
+ """
135
+ assert inference_params is not None and inference_params.seqlen_offset > 0
136
+ if self.rotary_emb_dim > 0:
137
+ self.rotary_emb._update_cos_sin_cache(
138
+ inference_params.max_seqlen, device=q.device, dtype=q.dtype
139
+ )
140
+ rotary_cos, rotary_sin = self.rotary_emb._cos_cached, self.rotary_emb._sin_cached
141
+ else:
142
+ rotary_cos, rotary_sin = None, None
143
+ batch = q.shape[0]
144
+ kv_cache, _ = inference_params.key_value_memory_dict[self.layer_idx]
145
+ kv_cache = kv_cache[:batch]
146
+ cache_seqlens = (
147
+ inference_params.lengths_per_sample[:batch]
148
+ if inference_params.lengths_per_sample is not None
149
+ else inference_params.seqlen_offset
150
+ )
151
+ assert flash_attn_with_kvcache is not None, "flash_attn must be installed"
152
+ context = flash_attn_with_kvcache(
153
+ q,
154
+ kv_cache[:, :, 0],
155
+ kv_cache[:, :, 1],
156
+ kv[:, :, 0],
157
+ kv[:, :, 1],
158
+ rotary_cos=rotary_cos,
159
+ rotary_sin=rotary_sin,
160
+ cache_seqlens=cache_seqlens,
161
+ softmax_scale=self.softmax_scale,
162
+ causal=self.causal,
163
+ rotary_interleaved=self.rotary_emb.interleaved if self.rotary_emb_dim > 0 else False,
164
+ )
165
+ return context
166
+
167
+ def _update_kvcache_attention(self, q, kv, inference_params):
168
+ """Write kv to inference_params, then do attention"""
169
+ if (
170
+ inference_params.seqlen_offset == 0
171
+ or flash_attn_with_kvcache is None
172
+ ):
173
+ # TODO: this only uses seqlen_offset and not lengths_per_sample.
174
+ kv = self._update_kv_cache(kv, inference_params)
175
+ k, v = kv.unbind(dim=-3)
176
+ k = torch.repeat_interleave(k, dim=2, repeats=self.num_heads // self.num_heads_kv)
177
+ v = torch.repeat_interleave(v, dim=2, repeats=self.num_heads // self.num_heads_kv)
178
+ return F.scaled_dot_product_attention(
179
+ q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2), is_causal=self.causal, scale=self.softmax_scale
180
+ ).transpose(1, 2)
181
+ else:
182
+ batch = q.shape[0]
183
+ kv_cache, _ = inference_params.key_value_memory_dict[self.layer_idx]
184
+ kv_cache = kv_cache[:batch]
185
+ cache_seqlens = (
186
+ inference_params.lengths_per_sample[:batch]
187
+ if inference_params.lengths_per_sample is not None
188
+ else inference_params.seqlen_offset
189
+ )
190
+ return flash_attn_with_kvcache(
191
+ q,
192
+ kv_cache[:, :, 0],
193
+ kv_cache[:, :, 1],
194
+ kv[:, :, 0],
195
+ kv[:, :, 1],
196
+ cache_seqlens=cache_seqlens,
197
+ softmax_scale=self.softmax_scale,
198
+ causal=self.causal,
199
+ )
200
+
201
+ def forward(self, x, inference_params=None):
202
+ """
203
+ Arguments:
204
+ x: (batch, seqlen, hidden_dim) (where hidden_dim = num heads * head dim) if
205
+ cu_seqlens is None and max_seqlen is None, else (total, hidden_dim) where total
206
+ is the is the sum of the sequence lengths in the batch.
207
+ inference_params: for generation. Adapted from Megatron-LM (and Apex)
208
+ https://github.com/NVIDIA/apex/blob/3ff1a10f72ec07067c4e44759442329804ac5162/apex/transformer/testing/standalone_transformer_lm.py#L470
209
+ """
210
+ if inference_params is not None and self.layer_idx not in inference_params.key_value_memory_dict:
211
+ inference_params.key_value_memory_dict[self.layer_idx] = self.allocate_inference_cache(
212
+ x.shape[0], inference_params.max_seqlen, dtype=x.dtype
213
+ )
214
+ seqlen_offset = (
215
+ 0
216
+ if inference_params is None
217
+ else (
218
+ inference_params.lengths_per_sample
219
+ if inference_params.lengths_per_sample is not None
220
+ else inference_params.seqlen_offset
221
+ )
222
+ )
223
+ rotary_max_seqlen = inference_params.max_seqlen if inference_params is not None else None
224
+ qkv = self.in_proj(x)
225
+ if self.mlp_dim > 0:
226
+ qkv, x_mlp = qkv.split([qkv.shape[-1] - self.mlp_dim, self.mlp_dim], dim=-1)
227
+ x_mlp_up, x_mlp_gate = x_mlp.chunk(2, dim=-1)
228
+ x_mlp = x_mlp_up * F.silu(x_mlp_gate)
229
+ if self.d_conv > 0:
230
+ # The inference code for conv1d is pretty messy, should clean it up
231
+ if (inference_params is None or inference_params.seqlen_offset == 0):
232
+ if causal_conv1d_fn is None:
233
+ qkv = rearrange(
234
+ self.conv1d(rearrange(qkv, "b s d -> b d s"))[..., :-(self.d_conv - 1)], "b d s -> b s d"
235
+ ).contiguous()
236
+ else:
237
+ qkv = causal_conv1d_fn(
238
+ qkv.transpose(1, 2),
239
+ rearrange(self.conv1d.weight, "d 1 w -> d w"),
240
+ self.conv1d.bias
241
+ ).transpose(1, 2)
242
+ if inference_params is not None:
243
+ _, conv_state = inference_params.key_value_memory_dict[self.layer_idx]
244
+ # If we just take qkv[:, :, -self.d_conv :], it will error if seqlen < self.d_conv
245
+ # Instead F.pad will pad with zeros if seqlen < self.d_conv, and truncate otherwise.
246
+ qkv_t = rearrange(qkv, "b l d -> b d l")
247
+ conv_state.copy_(F.pad(qkv_t, (self.d_conv - qkv_t.shape[-1], 0))) # Update state (B D W)
248
+ else:
249
+ _, conv_state = inference_params.key_value_memory_dict[self.layer_idx]
250
+ assert qkv.shape[1] == 1, "Only support decoding with 1 token at a time for now"
251
+ qkv = qkv.squeeze(1)
252
+ # Conv step
253
+ if causal_conv1d_update is None:
254
+ conv_state.copy_(torch.roll(conv_state, shifts=-1, dims=-1)) # Update state (B D W)
255
+ conv_state[:, :, -1] = qkv
256
+ qkv = torch.sum(conv_state * rearrange(self.conv1d.weight, "d 1 w -> d w"), dim=-1) # (B D)
257
+ if self.conv1d.bias is not None:
258
+ qkv = qkv + self.conv1d.bias
259
+ else:
260
+ qkv = causal_conv1d_update(
261
+ qkv,
262
+ conv_state,
263
+ rearrange(self.conv1d.weight, "d 1 w -> d w"),
264
+ self.conv1d.bias
265
+ )
266
+ qkv = qkv.unsqueeze(1)
267
+ q, kv = qkv.split([self.num_heads * self.head_dim, self.num_heads_kv * 2 * self.head_dim], dim=-1)
268
+ q = rearrange(q, "... (h d) -> ... h d", d=self.head_dim)
269
+ kv = rearrange(kv, "... (two hkv d) -> ... two hkv d", two=2, d=self.head_dim)
270
+ if (
271
+ inference_params is None
272
+ or inference_params.seqlen_offset == 0
273
+ or (self.rotary_emb_dim == 0 or self.rotary_emb_dim % 16 != 0)
274
+ ):
275
+ if self.rotary_emb_dim > 0:
276
+ q, kv = self.rotary_emb(
277
+ q, kv, seqlen_offset=seqlen_offset, max_seqlen=rotary_max_seqlen
278
+ )
279
+ if inference_params is None:
280
+ k, v = kv.unbind(dim=-3)
281
+ k = torch.repeat_interleave(k, dim=2, repeats=self.num_heads // self.num_heads_kv)
282
+ v = torch.repeat_interleave(v, dim=2, repeats=self.num_heads // self.num_heads_kv)
283
+ context = F.scaled_dot_product_attention(
284
+ q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2), is_causal=self.causal, scale=self.softmax_scale
285
+ ).transpose(1, 2)
286
+ else:
287
+ context = self._update_kvcache_attention(q, kv, inference_params)
288
+ else:
289
+ context = self._apply_rotary_update_kvcache_attention(q, kv, inference_params)
290
+ context = rearrange(context, "... h d -> ... (h d)")
291
+ if self.mlp_dim > 0:
292
+ context = torch.cat([context, x_mlp], dim=-1)
293
+ out = self.out_proj(context)
294
+ return out
vendor/mamba-og/mamba_ssm/modules/mlp.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024, Tri Dao, Albert Gu.
2
+ from torch import nn
3
+ from torch.nn import functional as F
4
+
5
+
6
+ class GatedMLP(nn.Module):
7
+ def __init__(
8
+ self,
9
+ in_features,
10
+ hidden_features=None,
11
+ out_features=None,
12
+ activation=F.silu,
13
+ bias=False,
14
+ multiple_of=128,
15
+ device=None,
16
+ dtype=None,
17
+ ):
18
+ factory_kwargs = {"device": device, "dtype": dtype}
19
+ super().__init__()
20
+ out_features = out_features if out_features is not None else in_features
21
+ hidden_features = (
22
+ hidden_features if hidden_features is not None else int(8 * in_features / 3)
23
+ )
24
+ hidden_features = (hidden_features + multiple_of - 1) // multiple_of * multiple_of
25
+ self.fc1 = nn.Linear(in_features, 2 * hidden_features, bias=bias, **factory_kwargs)
26
+ self.activation = activation
27
+ self.fc2 = nn.Linear(hidden_features, out_features, bias=bias, **factory_kwargs)
28
+
29
+ def forward(self, x):
30
+ y = self.fc1(x)
31
+ y, gate = y.chunk(2, dim=-1)
32
+ y = y * self.activation(gate)
33
+ y = self.fc2(y)
34
+ return y
vendor/mamba-og/mamba_ssm/modules/ssd_minimal.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024, Albert Gu and Tri Dao.
2
+ """Minimal implementation of SSD.
3
+
4
+ This is the same as Listing 1 from the paper.
5
+ """
6
+
7
+ import torch
8
+ import torch.nn.functional as F
9
+ from einops import rearrange, repeat
10
+
11
+ from mamba_ssm.ops.triton.ssd_combined import mamba_chunk_scan_combined
12
+
13
+
14
+ def segsum_unstable(x):
15
+ """Naive segment sum calculation."""
16
+ T = x.size(-1)
17
+ x_cumsum = torch.cumsum(x, dim=-1)
18
+ x_segsum = x_cumsum[..., :, None] - x_cumsum[..., None, :]
19
+ mask = torch.tril(torch.ones(T, T, device=x.device, dtype=bool), diagonal=0)
20
+ x_segsum = x_segsum.masked_fill(~mask, -torch.inf)
21
+ return x_segsum
22
+
23
+ def segsum(x):
24
+ """More stable segment sum calculation."""
25
+ T = x.size(-1)
26
+ x = repeat(x, "... d -> ... d e", e=T)
27
+ mask = torch.tril(torch.ones(T, T, device=x.device, dtype=bool), diagonal=-1)
28
+ x = x.masked_fill(~mask, 0)
29
+ x_segsum = torch.cumsum(x, dim=-2)
30
+ mask = torch.tril(torch.ones(T, T, device=x.device, dtype=bool), diagonal=0)
31
+ x_segsum = x_segsum.masked_fill(~mask, -torch.inf)
32
+ return x_segsum
33
+
34
+ def ssd_minimal_discrete(X, A, B, C, block_len, initial_states=None):
35
+ """
36
+ Arguments:
37
+ X: (batch, length, n_heads, d_head)
38
+ A: (batch, length, n_heads)
39
+ B: (batch, length, n_heads, d_state)
40
+ C: (batch, length, n_heads, d_state)
41
+ Return:
42
+ Y: (batch, length, n_heads, d_head)
43
+ """
44
+ assert X.dtype == A.dtype == B.dtype == C.dtype
45
+ assert X.shape[1] % block_len == 0
46
+
47
+ # Rearrange into blocks/chunks
48
+ X, A, B, C = [rearrange(x, "b (c l) ... -> b c l ...", l=block_len) for x in (X, A, B, C)]
49
+
50
+ A = rearrange(A, "b c l h -> b h c l")
51
+ A_cumsum = torch.cumsum(A, dim=-1)
52
+
53
+ # 1. Compute the output for each intra-chunk (diagonal blocks)
54
+ L = torch.exp(segsum(A))
55
+ Y_diag = torch.einsum("bclhn,bcshn,bhcls,bcshp->bclhp", C, B, L, X)
56
+
57
+ # 2. Compute the state for each intra-chunk
58
+ # (right term of low-rank factorization of off-diagonal blocks; B terms)
59
+ decay_states = torch.exp((A_cumsum[:, :, :, -1:] - A_cumsum))
60
+ states = torch.einsum("bclhn,bhcl,bclhp->bchpn", B, decay_states, X)
61
+
62
+ # 3. Compute the inter-chunk SSM recurrence; produces correct SSM states at chunk boundaries
63
+ # (middle term of factorization of off-diag blocks; A terms)
64
+ if initial_states is None:
65
+ initial_states = torch.zeros_like(states[:, :1])
66
+ states = torch.cat([initial_states, states], dim=1)
67
+ decay_chunk = torch.exp(segsum(F.pad(A_cumsum[:, :, :, -1], (1, 0))))
68
+ new_states = torch.einsum("bhzc,bchpn->bzhpn", decay_chunk, states)
69
+ states, final_state = new_states[:, :-1], new_states[:, -1]
70
+
71
+ # 4. Compute state -> output conversion per chunk
72
+ # (left term of low-rank factorization of off-diagonal blocks; C terms)
73
+ state_decay_out = torch.exp(A_cumsum)
74
+ Y_off = torch.einsum('bclhn,bchpn,bhcl->bclhp', C, states, state_decay_out)
75
+
76
+ # Add output of intra-chunk and inter-chunk terms (diagonal and off-diagonal blocks)
77
+ Y = rearrange(Y_diag+Y_off, "b c l h p -> b (c l) h p")
78
+ return Y, final_state
79
+
80
+
81
+ # Simple test
82
+ def test_correctness():
83
+ torch.manual_seed(42)
84
+
85
+ ## Dimensions
86
+ # Denoted (B, T, Q, D, P) in the paper
87
+ batch, seqlen, chunk_size, dim, headdim = 1, 2048, 64, 2048, 64
88
+ nheads = dim // headdim # (H) in the paper
89
+ ngroups = 1 # (G) in the paper
90
+ dstate = 64 # (N) in the paper
91
+ dtype = torch.float32
92
+ device = "cuda"
93
+
94
+ x = torch.randn(batch, seqlen, nheads, headdim, dtype=dtype, device=device)
95
+ dt = F.softplus(torch.randn(batch, seqlen, nheads, dtype=torch.float32, device=device) - 4).requires_grad_()
96
+ A = (-torch.exp(torch.rand(nheads, dtype=torch.float32, device=device))).requires_grad_()
97
+ B = torch.randn(batch, seqlen, ngroups, dstate, dtype=dtype, device=device)
98
+ C = torch.randn(batch, seqlen, ngroups, dstate, dtype=dtype, device=device)
99
+ D = torch.randn(nheads, dtype=dtype, device=device)
100
+
101
+ # Comparing fused version and minimal version
102
+ y = mamba_chunk_scan_combined(x, dt, A, B, C, chunk_size, D=None)
103
+ y_min, _ = ssd_minimal_discrete(x*dt.unsqueeze(-1), A*dt, B, C, chunk_size)
vendor/mamba-og/mamba_ssm/ops/__init__.py ADDED
File without changes
vendor/mamba-og/mamba_ssm/ops/cute/__init__.py ADDED
File without changes
vendor/mamba-og/mamba_ssm/ops/cute/mamba3/__init__.py ADDED
File without changes
vendor/mamba-og/mamba_ssm/ops/cute/mamba3/mamba3_step_fn.py ADDED
@@ -0,0 +1,933 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025, Tri Dao.
2
+ # Modified to use tvm-ffi and fake tensors instead of dlpack.
3
+ # Modified to optionally update state in place (state_out=None) or write to separate state_out.
4
+
5
+ import math
6
+ from typing import Optional, Type, Literal, List
7
+
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from torch import Tensor
11
+
12
+ import cuda.bindings.driver as cuda
13
+
14
+ import cutlass
15
+ import cutlass.cute as cute
16
+ from cutlass import Int32, Float32, Float16, BFloat16, Boolean, const_expr
17
+
18
+ from quack.compile_utils import make_fake_tensor
19
+ from quack.cute_dsl_utils import torch2cute_dtype_map
20
+
21
+
22
+ def transpose_view(a: cute.Tensor) -> cute.Tensor:
23
+ """Transpose the first two dimensions of a tensor on smem."""
24
+ shape = (a.shape[1], a.shape[0], *a.shape[2:])
25
+ order = (1, 0, *range(2, cute.rank(a)))
26
+ return cute.composition(a, cute.make_ordered_layout(shape, order=order))
27
+
28
+ def select(a: cute.Tensor, mode: List[int]) -> cute.Tensor:
29
+ return cute.make_tensor(a.iterator, cute.select(a.layout, mode))
30
+
31
+
32
+
33
+ def get_gmem_tiled_copy(dtype: Type[cutlass.Numeric], major_mode_size: int, num_threads: int, is_async: bool = True):
34
+ num_copy_bits = math.gcd(major_mode_size, 128 // dtype.width) * dtype.width
35
+ copy_elems = num_copy_bits // dtype.width
36
+ copy_op = cute.nvgpu.cpasync.CopyG2SOp() if is_async else cute.nvgpu.CopyUniversalOp()
37
+ copy_atom = cute.make_copy_atom(copy_op, dtype, num_bits_per_copy=num_copy_bits)
38
+ gmem_threads_per_row = major_mode_size // copy_elems
39
+ assert num_threads % gmem_threads_per_row == 0
40
+ thr_layout = cute.make_ordered_layout(
41
+ (num_threads // gmem_threads_per_row, gmem_threads_per_row),
42
+ order=(1, 0),
43
+ )
44
+ val_layout = cute.make_layout((1, copy_elems))
45
+ return cute.make_tiled_copy_tv(copy_atom, thr_layout, val_layout)
46
+
47
+
48
+ class Mamba3Step():
49
+ def __init__(self, tile_D: int, dstate: int, mimo: int = 1, num_warps: int = 4, remove_gate: bool = False, remove_outproj: bool = False):
50
+ assert num_warps >= 2
51
+ assert dstate % 8 == 0, "dstate must be multiple of 8" # for vectorized load /store
52
+ self.tile_D = tile_D
53
+ self.dstate = dstate
54
+ self.mimo = mimo
55
+ self.num_warps = num_warps
56
+ self.remove_gate = remove_gate
57
+ self.remove_outproj = remove_outproj
58
+
59
+ def _setup_smem_layouts(self):
60
+ self.sState_layout = cute.make_ordered_layout((self.tile_D, self.dstate), order=(1, 0))
61
+ # We don't need any swizzling for Bstate, B, C
62
+ self.sBC_layout = cute.make_ordered_layout((self.mimo, self.dstate), order=(1, 0))
63
+ # We don't need any swizzling for Xproj, Zproj, Outproj
64
+ self.sProj_layout = cute.make_ordered_layout((self.mimo, self.tile_D), order=(1, 0))
65
+
66
+ def _setup_gmem_tiled_copy(self, ):
67
+ num_threads = self.num_warps * cute.arch.WARP_SIZE
68
+ self.gmem_tiled_copy_state = get_gmem_tiled_copy(self.dtype, self.dstate, num_threads)
69
+ self.gmem_tiled_copy_BC = get_gmem_tiled_copy(self.b_dtype, self.dstate, num_threads)
70
+ self.gmem_tiled_copy_Proj = get_gmem_tiled_copy(self.proj_dtype, self.tile_D, num_threads)
71
+ # Gmem tiled copy for X, Z
72
+ # e.g. for tile_D = 64, we only want each thread loading 2 values
73
+ copy_elems_x = const_expr(min(4, cute.ceil_div(self.tile_D, cute.arch.WARP_SIZE)))
74
+ num_copy_bits_x = copy_elems_x * self.x_dtype.width
75
+ copy_atom_load_x = cute.make_copy_atom(
76
+ cute.nvgpu.CopyUniversalOp(), self.x_dtype, num_bits_per_copy=num_copy_bits_x
77
+ )
78
+ gmem_threads_per_row = self.tile_D // copy_elems_x
79
+ assert cute.arch.WARP_SIZE >= gmem_threads_per_row # Only 1 warp loads X, Z
80
+ self.gmem_tiled_copy_X = cute.make_tiled_copy_tv(
81
+ copy_atom_load_x, cute.make_layout(self.tile_D // copy_elems_x), cute.make_layout(copy_elems_x)
82
+ )
83
+
84
+
85
+ @cute.jit
86
+ def __call__(
87
+ # B: batch size, H: num heads, D: head dim, N: dstate, R: mimo
88
+ self,
89
+ mState: cute.Tensor, # (B, H, D, N)
90
+ mBstate: cute.Tensor, # (B, R, H, N)
91
+ mXstate: cute.Tensor, # (B, H, D)
92
+ mA: cute.Tensor, # (B, H)
93
+ mB: cute.Tensor, # (B, R, H, N)
94
+ mC: cute.Tensor, # (B, R, H, N)
95
+ mD: cute.Tensor, # (H)
96
+ mX: cute.Tensor, # (B, H, D)
97
+ mDt: cute.Tensor, # (B, H)
98
+ mTrap: cute.Tensor, # (B, H)
99
+ mXproj: cute.Tensor, # (R, H, D)
100
+ mOutproj: Optional[cute.Tensor], # (R, H, D), None if remove_outproj
101
+ mStateOut: cute.Tensor, # (B, H, D, N) — same as mState for in-place, or separate
102
+ mOut: cute.Tensor, # (B, H, D) or (B, R, H, D) if remove_outproj
103
+ mZ: Optional[cute.Tensor], # (B, H, D), None if remove_gate
104
+ mZproj: Optional[cute.Tensor], # (R, H, D), None if remove_gate
105
+ stream: cuda.CUstream,
106
+ ):
107
+ self.dtype = mState.element_type
108
+ self.b_dtype = mB.element_type
109
+ self.proj_dtype = mXproj.element_type
110
+ self.x_dtype = mX.element_type
111
+ assert mStateOut.element_type == self.dtype
112
+ assert mBstate.element_type == mB.element_type == mC.element_type
113
+ if const_expr(mOutproj is not None):
114
+ assert mXproj.element_type == mOutproj.element_type
115
+ if const_expr(mZ is not None):
116
+ assert mXproj.element_type == mZproj.element_type
117
+ assert mZ.element_type == self.x_dtype
118
+
119
+ self._setup_smem_layouts()
120
+ self._setup_gmem_tiled_copy()
121
+
122
+ # TV layout, this is the most important step as it decides which elements in B, C, State
123
+ # each thread will load from smem
124
+ num_threads = self.num_warps * cute.arch.WARP_SIZE
125
+ # TODO: these need to be adjusted based on dstate and tile_D
126
+ assert self.dstate in [32, 64, 128]
127
+ # TODO: This is not optimal for dstate=32 and 64, just to get sth quick to run
128
+ vecsize_dstate = 4 if self.dstate == 128 else 2 if self.dstate == 64 else 1
129
+ threads_per_dstate = self.dstate // vecsize_dstate
130
+ assert cute.arch.WARP_SIZE % threads_per_dstate == 0
131
+ num_groups = num_threads // threads_per_dstate
132
+ assert self.tile_D % num_groups == 0
133
+ lanes_per_D = self.tile_D // num_groups
134
+ copy_atom_state_s2r = cute.make_copy_atom(
135
+ cute.nvgpu.CopyUniversalOp(), mState.element_type, num_bits_per_copy=vecsize_dstate * mState.element_type.width
136
+ )
137
+ tiled_copy_state_s2r = cute.make_tiled_copy_tv(
138
+ copy_atom_state_s2r,
139
+ cute.make_ordered_layout((num_groups, threads_per_dstate), order=(1, 0)),
140
+ cute.make_ordered_layout((lanes_per_D, vecsize_dstate), order=(1, 0)),
141
+ )
142
+ copy_atom_B_s2r = cute.make_copy_atom(
143
+ cute.nvgpu.CopyUniversalOp(), mB.element_type, num_bits_per_copy=vecsize_dstate * mB.element_type.width
144
+ )
145
+ tiled_copy_B_s2r = cute.make_tiled_copy_tv(
146
+ copy_atom_B_s2r,
147
+ cute.make_ordered_layout((1, threads_per_dstate), order=(1, 0)),
148
+ cute.make_ordered_layout((1, vecsize_dstate), order=(1, 0)),
149
+ )
150
+
151
+ self.buffer_align_bytes = 1024
152
+
153
+ sZproj_size = cute.cosize(self.sProj_layout) if not self.remove_gate else 0
154
+ sOutproj_size = cute.cosize(self.sProj_layout) if not self.remove_outproj else 0
155
+
156
+ @cute.struct
157
+ class SharedStorage:
158
+ sX: cute.struct.Align[cute.struct.MemRange[Float32, self.tile_D], 128]
159
+ sXgamma: cute.struct.Align[cute.struct.MemRange[Float32, self.tile_D], 128]
160
+ sXstate: cute.struct.Align[cute.struct.MemRange[Float32, self.tile_D], 128]
161
+ sState: cute.struct.Align[
162
+ cute.struct.MemRange[self.dtype, cute.cosize(self.sState_layout)],
163
+ self.buffer_align_bytes,
164
+ ]
165
+ sBstate: cute.struct.Align[
166
+ cute.struct.MemRange[self.b_dtype, cute.cosize(self.sBC_layout)],
167
+ self.buffer_align_bytes,
168
+ ]
169
+ sB: cute.struct.Align[
170
+ cute.struct.MemRange[self.b_dtype, cute.cosize(self.sBC_layout)],
171
+ self.buffer_align_bytes,
172
+ ]
173
+ sC: cute.struct.Align[
174
+ cute.struct.MemRange[self.b_dtype, cute.cosize(self.sBC_layout)],
175
+ self.buffer_align_bytes,
176
+ ]
177
+ sXproj: cute.struct.Align[
178
+ cute.struct.MemRange[self.proj_dtype, cute.cosize(self.sProj_layout)],
179
+ self.buffer_align_bytes,
180
+ ]
181
+ sZproj: cute.struct.Align[
182
+ cute.struct.MemRange[self.proj_dtype, sZproj_size],
183
+ self.buffer_align_bytes,
184
+ ]
185
+ sOutproj: cute.struct.Align[
186
+ cute.struct.MemRange[self.proj_dtype, sOutproj_size],
187
+ self.buffer_align_bytes,
188
+ ]
189
+
190
+ self.shared_storage = SharedStorage
191
+
192
+ self.kernel(
193
+ mState,
194
+ mBstate,
195
+ mXstate,
196
+ mA,
197
+ mB,
198
+ mC,
199
+ mD,
200
+ mX,
201
+ mDt,
202
+ mTrap,
203
+ mXproj,
204
+ mOutproj,
205
+ mStateOut,
206
+ mOut,
207
+ mZ,
208
+ mZproj,
209
+ self.sState_layout,
210
+ self.sBC_layout,
211
+ self.sProj_layout,
212
+ self.gmem_tiled_copy_state,
213
+ self.gmem_tiled_copy_BC,
214
+ self.gmem_tiled_copy_Proj,
215
+ self.gmem_tiled_copy_X,
216
+ tiled_copy_state_s2r,
217
+ tiled_copy_B_s2r,
218
+ vecsize_dstate,
219
+ ).launch(
220
+ # grid: (d, h, b)
221
+ grid=[cute.ceil_div(mState.shape[2], self.tile_D), mState.shape[1], mState.shape[0]],
222
+ block=[num_threads, 1, 1],
223
+ stream=stream,
224
+ )
225
+
226
+ @cute.kernel
227
+ def kernel(
228
+ self,
229
+ mState: cute.Tensor, # (B, H, D, N)
230
+ mBstate: cute.Tensor, # (B, R, H, N)
231
+ mXstate: cute.Tensor, # (B, H, D)
232
+ mA: cute.Tensor, # (B, H)
233
+ mB: cute.Tensor, # (B, R, H, N)
234
+ mC: cute.Tensor, # (B, R, H, N)
235
+ mD: cute.Tensor, # (H)
236
+ mX: cute.Tensor, # (B, H, D)
237
+ mDt: cute.Tensor, # (B, H)
238
+ mTrap: cute.Tensor, # (B, H)
239
+ mXproj: cute.Tensor, # (R, H, D)
240
+ mOutproj: Optional[cute.Tensor], # (R, H, D), None if remove_outproj
241
+ mStateOut: cute.Tensor, # (B, H, D, N)
242
+ mOut: cute.Tensor, # (B, H, D) or (B, R, H, D) if remove_outproj
243
+ mZ: Optional[cute.Tensor], # (B, H, D), None if remove_gate
244
+ mZproj: Optional[cute.Tensor], # (R, H, D), None if remove_gate
245
+ sState_layout: cute.Layout | cute.ComposedLayout,
246
+ sBC_layout: cute.Layout | cute.ComposedLayout,
247
+ sProj_layout: cute.Layout | cute.ComposedLayout,
248
+ gmem_tiled_copy_state: cute.TiledCopy,
249
+ gmem_tiled_copy_BC: cute.TiledCopy,
250
+ gmem_tiled_copy_Proj: cute.TiledCopy,
251
+ gmem_tiled_copy_X: cute.TiledCopy,
252
+ tiled_copy_state_s2r: cute.TiledCopy,
253
+ tiled_copy_B_s2r: cute.TiledCopy,
254
+ vecsize_dstate: cutlass.Constexpr[int],
255
+ ):
256
+ tidx, _, _ = cute.arch.thread_idx()
257
+ bidd, bidh, bidb = cute.arch.block_idx()
258
+ warp_idx = cute.arch.make_warp_uniform(cute.arch.warp_idx())
259
+ lane_idx = cute.arch.lane_idx()
260
+
261
+ limit_d = mState.shape[2]
262
+
263
+ # ///////////////////////////////////////////////////////////////////////////////
264
+ # Slice for CTA
265
+ # ///////////////////////////////////////////////////////////////////////////////
266
+ # (tile_D, N)
267
+ gState, gStateOut = [
268
+ cute.local_tile(t[bidb, bidh, None, None], (self.tile_D, self.dstate), (bidd, 0))
269
+ for t in (mState, mStateOut)
270
+ ]
271
+ # (R, N)
272
+ gBstate, gB, gC = [
273
+ cute.local_tile(t[bidb, None, bidh, None], (self.mimo, self.dstate), (0, 0))
274
+ for t in (mBstate, mB, mC)
275
+ ]
276
+ # (tile_D,)
277
+ gXstate, gX = [
278
+ cute.local_tile(t[bidb, bidh, None], (self.tile_D,), (bidd,))
279
+ for t in (mXstate, mX)
280
+ ]
281
+ if const_expr(mOutproj is not None):
282
+ # Output is (B, H, D), outproj reduces MIMO rank
283
+ gOut = cute.local_tile(mOut[bidb, bidh, None], (self.tile_D,), (bidd,))
284
+ gXproj = cute.local_tile(mXproj[None, bidh, None], (self.mimo, self.tile_D), (0, bidd))
285
+ gOutproj = cute.local_tile(mOutproj[None, bidh, None], (self.mimo, self.tile_D), (0, bidd))
286
+ else:
287
+ # Output is (B, R, H, D), no outproj reduction
288
+ gXproj = cute.local_tile(mXproj[None, bidh, None], (self.mimo, self.tile_D), (0, bidd))
289
+ gOutproj = None
290
+ if const_expr(mZ is not None):
291
+ gZ = cute.local_tile(mZ[bidb, bidh, None], (self.tile_D,), (bidd,))
292
+ gZproj = cute.local_tile(mZproj[None, bidh, None], (self.mimo, self.tile_D), (0, bidd))
293
+
294
+ # ///////////////////////////////////////////////////////////////////////////////
295
+ # Generate smem tensors
296
+ # ///////////////////////////////////////////////////////////////////////////////
297
+ smem = cutlass.utils.SmemAllocator()
298
+ storage = smem.allocate(self.shared_storage)
299
+
300
+ sState = storage.sState.get_tensor(sState_layout)
301
+ sBstate = storage.sBstate.get_tensor(sBC_layout)
302
+ sB = storage.sB.get_tensor(sBC_layout)
303
+ sC = storage.sC.get_tensor(sBC_layout)
304
+ sXproj = storage.sXproj.get_tensor(sProj_layout)
305
+ sZproj = storage.sZproj.get_tensor(sProj_layout) if const_expr(mZ is not None) else None
306
+ sOutproj = storage.sOutproj.get_tensor(sProj_layout) if const_expr(mOutproj is not None) else None
307
+ sXstate = storage.sXstate.get_tensor(cute.make_layout(self.tile_D))
308
+ sX = storage.sX.get_tensor(cute.make_layout(self.tile_D))
309
+ sXgamma = storage.sXgamma.get_tensor(cute.make_layout(self.tile_D))
310
+
311
+ # ///////////////////////////////////////////////////////////////////////////////
312
+ # Partitioning using copy atoms
313
+ # ///////////////////////////////////////////////////////////////////////////////
314
+ gmem_thr_copy_state = gmem_tiled_copy_state.get_slice(tidx)
315
+ # copying states from r2g uses the same tiled copy as s2r
316
+ gmem_thr_copy_StateOut = tiled_copy_state_s2r.get_slice(tidx)
317
+ gmem_thr_copy_BC = gmem_tiled_copy_BC.get_slice(tidx)
318
+ gmem_thr_copy_Proj = gmem_tiled_copy_Proj.get_slice(tidx)
319
+ gmem_thr_copy_X = gmem_tiled_copy_X.get_slice(lane_idx) # Only 1 warp loads X, Z
320
+
321
+ tSgS = gmem_thr_copy_state.partition_S(gState)
322
+ tSsS_g2s = gmem_thr_copy_state.partition_D(sState)
323
+ tSgSOut = gmem_thr_copy_StateOut.partition_D(gStateOut)
324
+ tBCgBstate = gmem_thr_copy_BC.partition_S(gBstate)
325
+ tBCsBstate = gmem_thr_copy_BC.partition_D(sBstate)
326
+ tBCgB = gmem_thr_copy_BC.partition_S(gB)
327
+ tBCsB = gmem_thr_copy_BC.partition_D(sB)
328
+ tBCgC = gmem_thr_copy_BC.partition_S(gC)
329
+ tBCsC = gmem_thr_copy_BC.partition_D(sC)
330
+ tPgXproj = gmem_thr_copy_Proj.partition_S(gXproj)
331
+ tPsXproj = gmem_thr_copy_Proj.partition_D(sXproj)
332
+ if const_expr(mZ is not None):
333
+ tPgZproj = gmem_thr_copy_Proj.partition_S(gZproj)
334
+ tPsZproj = gmem_thr_copy_Proj.partition_D(sZproj)
335
+ if const_expr(mOutproj is not None):
336
+ tPgOutproj = gmem_thr_copy_Proj.partition_S(gOutproj)
337
+ tPsOutproj = gmem_thr_copy_Proj.partition_D(sOutproj)
338
+ tXgX = gmem_thr_copy_X.partition_S(gX)
339
+ tXsX = gmem_thr_copy_X.partition_D(sX)
340
+ tXsXgamma = gmem_thr_copy_X.partition_D(sXgamma)
341
+ tXgXstate = gmem_thr_copy_X.partition_S(gXstate)
342
+ tXsXstate = gmem_thr_copy_X.partition_D(sXstate)
343
+
344
+ # Idk why this order of threads_per_dstate and num_groups are reversed
345
+ threads_per_dstate, num_groups = tiled_copy_state_s2r.layout_tv_tiled[0].shape
346
+ lanes_per_D = self.tile_D // num_groups
347
+
348
+ # For bound checking
349
+ cS = cute.make_identity_tensor((self.tile_D, self.dstate))
350
+ tScS = gmem_thr_copy_state.partition_S(cS)
351
+ cBC = cute.make_identity_tensor((self.mimo, self.dstate))
352
+ tBCcBC = gmem_thr_copy_BC.partition_S(cBC)
353
+ cProj = cute.make_identity_tensor((self.mimo, self.tile_D))
354
+ tPcProj = gmem_thr_copy_Proj.partition_S(cProj)
355
+
356
+ A_val = Float32(mA[bidb, bidh])
357
+ dt_val = Float32(mDt[bidb, bidh])
358
+ trap_val = Float32(mTrap[bidb, bidh])
359
+
360
+ # Load X and Xstate, these are small so we want to kick them off first
361
+ tXrX = cute.make_rmem_tensor_like(tXgX)
362
+ tXrXstate = cute.make_rmem_tensor_like(tXgXstate)
363
+ copy_elems_x = cute.size(tXgX.shape[0][0])
364
+ assert cute.size(tXgX.shape) == copy_elems_x # Only 1 load instruction
365
+ num_loads_X = const_expr(self.tile_D // copy_elems_x)
366
+ need_bound_check_X = const_expr(cute.arch.WARP_SIZE > num_loads_X)
367
+ if warp_idx == 0:
368
+ if not need_bound_check_X or lane_idx < num_loads_X:
369
+ cute.copy(gmem_tiled_copy_X, tXgX, tXrX)
370
+ if warp_idx == 1:
371
+ if not need_bound_check_X or lane_idx < num_loads_X:
372
+ cute.copy(gmem_tiled_copy_X, tXgXstate, tXrXstate)
373
+
374
+ # Load Bstate, B, Xproj need bound checking
375
+ for m in cutlass.range(cute.size(tBCcBC.shape[1]), unroll_full=True):
376
+ if tBCcBC[0, m, 0][0] < self.mimo:
377
+ cute.copy(gmem_tiled_copy_BC, tBCgBstate[None, m, None], tBCsBstate[None, m, None])
378
+ cute.copy(gmem_tiled_copy_BC, tBCgB[None, m, None], tBCsB[None, m, None])
379
+ for m in cutlass.range(cute.size(tPcProj.shape[1]), unroll_full=True):
380
+ if tPcProj[0, m, 0][0] < self.mimo:
381
+ cute.copy(gmem_tiled_copy_Proj, tPgXproj[None, m, None], tPsXproj[None, m, None])
382
+ cute.arch.cp_async_commit_group()
383
+
384
+ # Load State, not doing any bound check for now
385
+ cute.copy(gmem_tiled_copy_state, tSgS, tSsS_g2s)
386
+ cute.arch.cp_async_commit_group()
387
+
388
+ alpha_val = cute.math.exp(A_val * dt_val, fastmath=True)
389
+ # Transform X and Xstate by multiplying with gamma and beta, then write to smem
390
+ if warp_idx == 0:
391
+ tXrX_f32 = cute.make_rmem_tensor_like(tXrX, Float32)
392
+ tXrX_f32.store(tXrX.load().to(Float32))
393
+ if not need_bound_check_X or lane_idx < num_loads_X:
394
+ cute.autovec_copy(tXrX_f32, tXsX)
395
+ gamma_val = trap_val * dt_val
396
+ tXrX_f32.store(tXrX_f32.load() * gamma_val)
397
+ if not need_bound_check_X or lane_idx < num_loads_X:
398
+ cute.autovec_copy(tXrX_f32, tXsXgamma)
399
+ if warp_idx == 1:
400
+ beta_val = (1.0 - trap_val) * dt_val * alpha_val
401
+ tXrXstate_f32 = cute.make_rmem_tensor_like(tXgXstate, Float32)
402
+ tXrXstate_f32.store(tXrXstate.load().to(Float32) * beta_val)
403
+ if not need_bound_check_X or lane_idx < num_loads_X:
404
+ cute.autovec_copy(tXrXstate_f32, tXsXstate)
405
+
406
+ # Load C, need bound checking
407
+ for m in cutlass.range(cute.size(tBCcBC.shape[1]), unroll_full=True):
408
+ if tBCcBC[0, m, 0][0] < self.mimo:
409
+ cute.copy(gmem_tiled_copy_BC, tBCgC[None, m, None], tBCsC[None, m, None])
410
+ cute.arch.cp_async_commit_group()
411
+
412
+ cute.arch.cp_async_wait_group(2) # B, Bstate, Xproj are done loading
413
+ cute.arch.sync_threads()
414
+ # Load B, Bstate, Xproj from smem
415
+ smem_thr_copy_B = tiled_copy_B_s2r.get_slice(tidx % threads_per_dstate)
416
+ # ((vecsize_dstate, 1), mimo, 1) -> ((vecsize_dstate, 1), mimo)
417
+ tSsB = smem_thr_copy_B.partition_S(sB)[None, None, 0]
418
+ tSsBstate = smem_thr_copy_B.partition_S(sBstate)[None, None, 0]
419
+ tSrB = cute.make_rmem_tensor_like(tSsB)
420
+ tSrBstate = cute.make_rmem_tensor_like(tSsBstate)
421
+ cute.autovec_copy(tSsB, tSrB)
422
+ cute.autovec_copy(tSsBstate, tSrBstate)
423
+ tSrB_f32 = cute.make_rmem_tensor_like(tSrB, Float32)
424
+ tSrB_f32.store(tSrB.load().to(Float32))
425
+ tSrBstate_f32 = cute.make_rmem_tensor_like(tSrBstate, Float32)
426
+ tSrBstate_f32.store(tSrBstate.load().to(Float32))
427
+ # Loading x and xstate, at most 1 val per thread
428
+ x_val = Float32(0.0)
429
+ if lane_idx < lanes_per_D:
430
+ # TODO: should this be warp_idx or group_idx?
431
+ x_val = sXgamma[warp_idx * lanes_per_D + lane_idx]
432
+ x_state_val = Float32(0.0)
433
+ if lane_idx < lanes_per_D:
434
+ x_state_val = sXstate[warp_idx * lanes_per_D + lane_idx]
435
+
436
+ new_state = cute.make_rmem_tensor((vecsize_dstate, lanes_per_D), Float32)
437
+ for r in cutlass.range_constexpr(self.mimo):
438
+ x_proj_val = Float32(0.0)
439
+ if lane_idx < lanes_per_D:
440
+ x_proj_val = Float32(sXproj[r, warp_idx * lanes_per_D + lane_idx])
441
+ x_gamma_x_proj_val = x_val * x_proj_val
442
+ x_state_x_proj_val = x_state_val * x_proj_val
443
+ for d in cutlass.range(lanes_per_D, unroll_full=True):
444
+ xg = cute.arch.shuffle_sync(x_gamma_x_proj_val, d)
445
+ xb = cute.arch.shuffle_sync(x_state_x_proj_val, d)
446
+ for v in cutlass.range(vecsize_dstate, unroll_full=True):
447
+ if const_expr(r == 0):
448
+ new_state[v, d] = xg * tSrB_f32[v, r]
449
+ else:
450
+ new_state[v, d] += xg * tSrB_f32[v, r]
451
+ new_state[v, d] += xb * tSrBstate_f32[v, r]
452
+
453
+ cute.arch.cp_async_wait_group(1) # state is done loading
454
+ cute.arch.sync_threads()
455
+ thr_copy_state_s2r = tiled_copy_state_s2r.get_slice(tidx)
456
+ # ((vecsize_state, lanes_per_D), 1, 1)
457
+ tSsS = thr_copy_state_s2r.partition_S(sState)
458
+ tSrS = cute.make_rmem_tensor_like(tSsS)
459
+ cute.autovec_copy(tSsS, tSrS)
460
+
461
+ # ((vecsize_state, lanes_per_D), 1, 1)
462
+ # tSrS_f32 = cute.make_rmem_tensor_like(tSrS, Float32)
463
+ tSrS_f32 = cute.make_rmem_tensor(((vecsize_dstate, 1), lanes_per_D, 1), Float32)
464
+ assert cute.size(tSrS.shape) == cute.size(tSrS_f32.shape)
465
+ tSrS_f32.store(tSrS.load().to(Float32))
466
+ for v in cutlass.range(cute.size(tSrS_f32), unroll_full=True):
467
+ tSrS_f32[v] = tSrS_f32[v] * alpha_val + new_state[v]
468
+ tSrS.store(tSrS_f32.load().to(self.dtype))
469
+
470
+ # Load Z from gmem -> rmem, it's small, at most 1 val per thread
471
+ if const_expr(mZ is not None):
472
+ z_val = Float32(0.0)
473
+ if lane_idx < lanes_per_D:
474
+ z_val = Float32(gZ[warp_idx * lanes_per_D + lane_idx])
475
+ # Load Zproj and Outproj, need bound checking
476
+ for m in cutlass.range(cute.size(tPcProj.shape[1]), unroll_full=True):
477
+ if tPcProj[0, m, 0][0] < self.mimo:
478
+ if const_expr(mZ is not None):
479
+ cute.copy(gmem_tiled_copy_Proj, tPgZproj[None, m, None], tPsZproj[None, m, None])
480
+ if const_expr(mOutproj is not None):
481
+ cute.copy(gmem_tiled_copy_Proj, tPgOutproj[None, m, None], tPsOutproj[None, m, None])
482
+ cute.arch.cp_async_commit_group()
483
+
484
+ # Write state back to StateOut (may be same memory as State for in-place)
485
+ cute.copy(tiled_copy_state_s2r, tSrS, tSgSOut)
486
+
487
+ # Do state @ C
488
+ cute.arch.cp_async_wait_group(1) # C is done loading
489
+ cute.arch.sync_threads()
490
+ # ((vecsize_dstate, 1), mimo, 1) -> ((vecsize_dstate, 1), 1, mimo)
491
+ tSsC = select(smem_thr_copy_B.partition_S(sC), mode=[0, 2, 1])
492
+ tSrC = cute.make_rmem_tensor_like(tSsC)
493
+ cute.autovec_copy(tSsC, tSrC)
494
+ tSrC_f32 = cute.make_rmem_tensor_like(tSrC, Float32)
495
+ tSrC_f32.store(tSrC.load().to(Float32))
496
+ out_expanded = cute.make_rmem_tensor((lanes_per_D, self.mimo), Float32)
497
+ # tSrS_f32 has shape ((vecsize_dstate, 1), lanes_per_D, 1)
498
+ # tSrC has shape ((vecsize_dstate, 1), mimo)
499
+ out_expanded.store(
500
+ (tSrS_f32.load() * tSrC_f32.load()).reduce(cute.ReductionOp.ADD, init_val=0.0, reduction_profile=(0, None, None))
501
+ )
502
+ assert lanes_per_D <= threads_per_dstate
503
+ for d in cutlass.range(lanes_per_D, unroll_full=True):
504
+ for r in cutlass.range(self.mimo, unroll_full=True):
505
+ out_expanded[d, r] += cute.arch.shuffle_sync_bfly(out_expanded[d, r], offset=16)
506
+ for i in cutlass.range_constexpr(int(math.log2(lanes_per_D))):
507
+ step = 1 << (int(math.log2(lanes_per_D)) - 1 - i)
508
+ should_swap = not Boolean(lane_idx & step)
509
+ for j in cutlass.range_constexpr(step):
510
+ for r in cutlass.range(self.mimo, unroll_full=True):
511
+ lower, upper = out_expanded[j, r], out_expanded[j + step, r]
512
+ out_expanded[j, r] = upper if should_swap else lower
513
+ out_expanded[j + step, r] = lower if should_swap else upper
514
+ shfl_val = cute.arch.shuffle_sync_bfly(out_expanded[j, r], offset=step)
515
+ out_expanded[j, r] = shfl_val + out_expanded[j + step, r]
516
+ # After this, the out values are just out_expanded[0, None]
517
+ out = out_expanded[0, None] # (mimo,)
518
+
519
+ # Add D * x * x_proj to out
520
+ D_val = Float32(mD[bidh])
521
+ x_val = Float32(0.0)
522
+ if lane_idx < lanes_per_D:
523
+ x_val = sX[warp_idx * lanes_per_D + lane_idx]
524
+ for r in cutlass.range_constexpr(self.mimo):
525
+ x_proj_val = Float32(0.0)
526
+ if lane_idx < lanes_per_D:
527
+ x_proj_val = Float32(sXproj[r, warp_idx * lanes_per_D + lane_idx])
528
+ out[r] += D_val * x_val * x_proj_val
529
+
530
+ cute.arch.cp_async_wait_group(0) # Zproj and Outproj are done loading
531
+ cute.arch.sync_threads()
532
+
533
+ if const_expr(mOutproj is not None):
534
+ # Gate: z_r * sigmoid(z_r)
535
+ if const_expr(mZ is not None):
536
+ for r in cutlass.range_constexpr(self.mimo):
537
+ z_proj_val = Float32(0.0)
538
+ if lane_idx < lanes_per_D:
539
+ z_proj_val = Float32(sZproj[r, warp_idx * lanes_per_D + lane_idx])
540
+ z_r_half = 0.5 * (z_val * z_proj_val)
541
+ z_r_silu = z_r_half * cute.math.tanh(z_r_half, fastmath=True) + z_r_half
542
+ out[r] *= z_r_silu
543
+
544
+ # Final projection along mimo dim
545
+ out_val = Float32(0.0)
546
+ for r in cutlass.range_constexpr(self.mimo):
547
+ out_proj_val = Float32(0.0)
548
+ if lane_idx < lanes_per_D:
549
+ out_proj_val = Float32(sOutproj[r, warp_idx * lanes_per_D + lane_idx])
550
+ if const_expr(r == 0):
551
+ out_val = out[r] * out_proj_val
552
+ else:
553
+ out_val += out[r] * out_proj_val
554
+
555
+ # Write final output (B, H, D)
556
+ if lane_idx < lanes_per_D:
557
+ gOut[warp_idx * lanes_per_D + lane_idx] = out_val.to(mOut.element_type)
558
+ else:
559
+ # No outproj: write per-rank output (B, R, H, D)
560
+ for r in cutlass.range_constexpr(self.mimo):
561
+ gOut_r = cute.local_tile(mOut[bidb, r, bidh, None], (self.tile_D,), (bidd,))
562
+ if lane_idx < lanes_per_D:
563
+ gOut_r[warp_idx * lanes_per_D + lane_idx] = out[r].to(mOut.element_type)
564
+
565
+
566
+ def mamba3_step_fn(
567
+ # B: batch size, H: num heads, D: head dim, N: dstate, R: mimo
568
+ state: Tensor, # (B, H, D, N) — updated in place if state_out is None
569
+ Bstate: Tensor, # (B, R, H, N)
570
+ Xstate: Tensor, # (B, H, D)
571
+ A: Tensor, # (B, H)
572
+ B: Tensor, # (B, R, H, N)
573
+ C: Tensor, # (B, R, H, N)
574
+ D: Tensor, # (H)
575
+ x: Tensor, # (B, H, D)
576
+ dt: Tensor, # (B, H)
577
+ trap: Tensor, # (B, H)
578
+ xproj: Tensor, # (R, H, D)
579
+ outproj: Optional[Tensor] = None, # (R, H, D), None if remove_outproj
580
+ state_out: Optional[Tensor] = None, # (B, H, D, N), None for in-place update
581
+ out: Tensor = None, # (B, H, D) or (B, R, H, D) if remove_outproj
582
+ z: Optional[Tensor] = None, # (B, H, D), None if remove_gate
583
+ zproj: Optional[Tensor] = None, # (R, H, D), None if remove_gate
584
+ tile_D: int = 64,
585
+ num_warps: int = 2,
586
+ ) -> None:
587
+ has_z = z is not None
588
+ has_outproj = outproj is not None
589
+ inplace = state_out is None
590
+ batch, nheads, hdim, dstate = state.shape
591
+ mimo = Bstate.shape[1]
592
+ assert state.shape == (batch, nheads, hdim, dstate)
593
+ assert Bstate.shape == (batch, mimo, nheads, dstate)
594
+ assert Xstate.shape == (batch, nheads, hdim)
595
+ assert A.shape == (batch, nheads)
596
+ assert B.shape == (batch, mimo, nheads, dstate)
597
+ assert C.shape == (batch, mimo, nheads, dstate)
598
+ assert D.shape == (nheads,)
599
+ assert x.shape == (batch, nheads, hdim)
600
+ if has_z:
601
+ assert z.shape == (batch, nheads, hdim)
602
+ assert zproj is not None
603
+ assert zproj.shape == (mimo, nheads, hdim)
604
+ assert dt.shape == (batch, nheads)
605
+ assert trap.shape == (batch, nheads)
606
+ assert xproj.shape == (mimo, nheads, hdim)
607
+ xproj = xproj.contiguous()
608
+ if has_outproj:
609
+ assert outproj.shape == (mimo, nheads, hdim)
610
+ assert out.shape == (batch, nheads, hdim)
611
+ else:
612
+ assert out.shape == (batch, mimo, nheads, hdim)
613
+
614
+ # Use state itself as output target when in-place
615
+ if inplace:
616
+ state_out = state
617
+ else:
618
+ assert state_out.shape == (batch, nheads, hdim, dstate)
619
+
620
+ required_tensors = [state, Bstate, Xstate, A, B, C, D, x, dt, trap, xproj, state_out, out]
621
+ if has_outproj:
622
+ required_tensors.append(outproj)
623
+ if has_z:
624
+ required_tensors.extend([z, zproj])
625
+ assert all(t.is_cuda for t in required_tensors)
626
+ assert state.dtype in [torch.float16, torch.bfloat16, torch.float32], "Unsupported input dtype"
627
+
628
+ # Map torch dtypes to cutlass dtypes
629
+ state_cute_dtype = torch2cute_dtype_map[state.dtype]
630
+ b_cute_dtype = torch2cute_dtype_map[Bstate.dtype]
631
+ x_cute_dtype = torch2cute_dtype_map[x.dtype]
632
+ proj_cute_dtype = torch2cute_dtype_map[xproj.dtype]
633
+ a_cute_dtype = torch2cute_dtype_map[A.dtype]
634
+ d_cute_dtype = torch2cute_dtype_map[D.dtype]
635
+ dt_cute_dtype = torch2cute_dtype_map[dt.dtype]
636
+ trap_cute_dtype = torch2cute_dtype_map[trap.dtype]
637
+
638
+ compile_key = (
639
+ tile_D,
640
+ num_warps,
641
+ dstate,
642
+ hdim,
643
+ mimo,
644
+ state.dtype,
645
+ Bstate.dtype,
646
+ xproj.dtype,
647
+ A.dtype,
648
+ D.dtype,
649
+ dt.dtype,
650
+ trap.dtype,
651
+ has_z,
652
+ has_outproj,
653
+ )
654
+ if compile_key not in mamba3_step_fn.compile_cache:
655
+ mamba3_step_op = Mamba3Step(tile_D, dstate, mimo, num_warps, remove_gate=not has_z, remove_outproj=not has_outproj)
656
+
657
+ # Create symbolic dimensions for batch and nheads
658
+ batch_sym = cute.sym_int()
659
+ nheads_sym = cute.sym_int()
660
+
661
+ # Divisibility for strides (128-bit alignment)
662
+ div_state = 128 // state_cute_dtype.width
663
+ div_b = 128 // b_cute_dtype.width
664
+ div_x = 128 // x_cute_dtype.width
665
+ div_proj = 128 // proj_cute_dtype.width
666
+ div_a = 128 // a_cute_dtype.width
667
+ div_d = 128 // d_cute_dtype.width
668
+ div_dt = 128 // dt_cute_dtype.width
669
+ div_trap = 128 // trap_cute_dtype.width
670
+
671
+ # Create fake tensors with symbolic batch/nheads dimensions
672
+ state_fake = make_fake_tensor(state_cute_dtype, (batch_sym, nheads_sym, hdim, dstate), div_state)
673
+ Bstate_fake = make_fake_tensor(b_cute_dtype, (batch_sym, mimo, nheads_sym, dstate), div_b)
674
+ Xstate_fake = make_fake_tensor(x_cute_dtype, (batch_sym, nheads_sym, hdim), div_x)
675
+ A_fake = make_fake_tensor(a_cute_dtype, (batch_sym, nheads_sym), div_a)
676
+ B_fake = make_fake_tensor(b_cute_dtype, (batch_sym, mimo, nheads_sym, dstate), div_b)
677
+ C_fake = make_fake_tensor(b_cute_dtype, (batch_sym, mimo, nheads_sym, dstate), div_b)
678
+ D_fake = make_fake_tensor(d_cute_dtype, (nheads_sym,), div_d)
679
+ x_fake = make_fake_tensor(x_cute_dtype, (batch_sym, nheads_sym, hdim), div_x)
680
+ dt_fake = make_fake_tensor(dt_cute_dtype, (batch_sym, nheads_sym), div_dt)
681
+ trap_fake = make_fake_tensor(trap_cute_dtype, (batch_sym, nheads_sym), div_trap)
682
+ xproj_fake = make_fake_tensor(proj_cute_dtype, (mimo, nheads_sym, hdim), div_proj)
683
+ outproj_fake = make_fake_tensor(proj_cute_dtype, (mimo, nheads_sym, hdim), div_proj) if has_outproj else None
684
+ state_out_fake = make_fake_tensor(state_cute_dtype, (batch_sym, nheads_sym, hdim, dstate), div_state)
685
+ if has_outproj:
686
+ out_fake = make_fake_tensor(x_cute_dtype, (batch_sym, nheads_sym, hdim), div_x)
687
+ else:
688
+ out_fake = make_fake_tensor(x_cute_dtype, (batch_sym, mimo, nheads_sym, hdim), div_x)
689
+ z_fake = make_fake_tensor(x_cute_dtype, (batch_sym, nheads_sym, hdim), div_x) if has_z else None
690
+ zproj_fake = make_fake_tensor(proj_cute_dtype, (mimo, nheads_sym, hdim), div_proj) if has_z else None
691
+
692
+ fake_stream = cute.runtime.make_fake_stream(use_tvm_ffi_env_stream=True)
693
+
694
+ mamba3_step_fn.compile_cache[compile_key] = cute.compile(
695
+ mamba3_step_op,
696
+ state_fake,
697
+ Bstate_fake,
698
+ Xstate_fake,
699
+ A_fake,
700
+ B_fake,
701
+ C_fake,
702
+ D_fake,
703
+ x_fake,
704
+ dt_fake,
705
+ trap_fake,
706
+ xproj_fake,
707
+ outproj_fake,
708
+ state_out_fake,
709
+ out_fake,
710
+ z_fake,
711
+ zproj_fake,
712
+ fake_stream,
713
+ options="--enable-tvm-ffi",
714
+ )
715
+
716
+ # Call with real PyTorch tensors directly (no dlpack conversion needed)
717
+ # When inplace, state_out is state (set above)
718
+ mamba3_step_fn.compile_cache[compile_key](
719
+ state,
720
+ Bstate,
721
+ Xstate,
722
+ A,
723
+ B,
724
+ C,
725
+ D,
726
+ x,
727
+ dt,
728
+ trap,
729
+ xproj,
730
+ outproj,
731
+ state_out,
732
+ out,
733
+ z,
734
+ zproj,
735
+ )
736
+
737
+
738
+ mamba3_step_fn.compile_cache = {}
739
+
740
+
741
+ def selective_state_update_fused_ref_v2(
742
+ state, A, B, C, xproj, x, zproj, z, dt, B_state, x_state, trap, D, outproj,
743
+ compute_dtype=torch.float32
744
+ ):
745
+ """
746
+ Reference to match the new fused kernel API.
747
+
748
+ Shapes:
749
+ state: (B, N, H, S)
750
+ A: (B, N)
751
+ B: (B, R, N, S)
752
+ C: (B, R, N, S)
753
+ xproj: (R, N, H)
754
+ x: (B, N, H)
755
+ zproj: (R, N, H)
756
+ z: (B, N, H)
757
+ dt: (B, N)
758
+ B_state: (B, R, N, S)
759
+ x_state: (B, N, H)
760
+ trap: (B, N)
761
+ D: (N,)
762
+ outproj: (R, N, H)
763
+
764
+ Returns:
765
+ out: (B, N, H)
766
+ new_state: (B, N, H, S)
767
+ """
768
+ Bsz, N, H, S = state.shape
769
+ R = B.shape[1]
770
+
771
+ # Dtypes for numerics (match kernel's fp32 accum)
772
+ og_dtype = state.dtype
773
+ A_f = A.to(compute_dtype) # (B, N)
774
+ dt_f = dt.to(compute_dtype) # (B, N)
775
+ trap_f = trap.to(compute_dtype) # (B, N)
776
+ D_f = D.to(compute_dtype) # (N,)
777
+ x_f = x.to(compute_dtype) # (B, N, H)
778
+ xst_f = x_state.to(compute_dtype) # (B, N, H)
779
+ B_f = B.to(compute_dtype) # (B, R, N, S)
780
+ C_f = C.to(compute_dtype) # (B, R, N, S)
781
+ Bst_f = B_state.to(compute_dtype) # (B, R, N, S)
782
+ Xp_f = xproj.to(compute_dtype) # (R, N, H)
783
+ st_f = state.to(compute_dtype) # (B, N, H, S)
784
+
785
+ alpha = torch.exp(A_f * dt_f) # (B, N)
786
+ beta = (1.0 - trap_f) * dt_f * alpha # (B, N)
787
+ gamma = trap_f * dt_f # (B, N)
788
+
789
+ x_vals = (x_f[:, None, :, :] * Xp_f[None, :, :, :]) # (B, R, N, H)
790
+ xs_vals = (xst_f[:, None, :, :] * Xp_f[None, :, :, :]) # (B, R, N, H)
791
+
792
+ xBt_state = torch.einsum('brnh,brns->bnhs', x_vals * gamma.unsqueeze(-1).unsqueeze(1), B_f)
793
+ xBt_prev = torch.einsum('brnh,brns->bnhs', xs_vals * beta.unsqueeze(-1).unsqueeze(1), Bst_f)
794
+
795
+ new_state = st_f * alpha[:, :, None, None] + xBt_state + xBt_prev # (B, N, H, S)
796
+
797
+ out_r = torch.einsum('bnhs,brns->brnh', new_state, C_f) # (B, R, N, H)
798
+
799
+ out_r = out_r + (x_vals * D_f[None, :, None]) # (B, R, N, H)
800
+
801
+ if z is not None:
802
+ z_f = z.to(compute_dtype) # (B, N, H)
803
+ Zp_f = zproj.to(compute_dtype) # (R, N, H)
804
+ z_vals = (z_f[:, None, :, :] * Zp_f[None, :, :, :]) # (B, R, N, H)
805
+ out_r = out_r * z_vals * torch.sigmoid(z_vals) # (B, R, N, H)
806
+
807
+ if outproj is not None:
808
+ Op_f = outproj.to(compute_dtype) # (R, N, H)
809
+ out = torch.einsum('brnh,rnh->bnh', out_r, Op_f) # (B, N, H)
810
+ else:
811
+ out = out_r # (B, R, N, H)
812
+
813
+ return out.to(og_dtype), new_state.to(og_dtype)
814
+
815
+
816
+ def _bytes_of(t):
817
+ return t.numel() * t.element_size()
818
+
819
+
820
+ if __name__ == "__main__":
821
+ torch.manual_seed(1357)
822
+ batch, nheads, hdim, dstate, mimo = 128, 64, 64, 128, 4
823
+ device = torch.device("cuda:0")
824
+ dtype_state = torch.float32
825
+ dtype = torch.float32
826
+ state = torch.randn(batch, nheads, hdim, dstate, device=device, dtype=dtype_state)
827
+ Bstate = torch.randn(batch, mimo, nheads, dstate, device=device, dtype=dtype)
828
+ Xstate = torch.randn(batch, nheads, hdim, device=device, dtype=dtype)
829
+ A = -F.softplus(torch.randn(batch, nheads, device=device, dtype=torch.float32))
830
+ B = torch.randn(batch, mimo, nheads, dstate, device=device, dtype=dtype)
831
+ C = torch.randn(batch, mimo, nheads, dstate, device=device, dtype=dtype)
832
+ D = torch.randn(nheads, device=device, dtype=dtype)
833
+ x = torch.randn(batch, nheads, hdim, device=device, dtype=dtype)
834
+ z = torch.randn(batch, nheads, hdim, device=device, dtype=dtype)
835
+ dt = torch.exp(torch.rand(nheads, device=device) * (math.log(0.1) - math.log(0.001)) + math.log(0.001))
836
+ dt = torch.clamp(dt, min=1e-4)
837
+ dt_bias = dt + torch.log(-torch.expm1(-dt))
838
+ dt = F.softplus(torch.randn(batch, nheads, device=device) + dt_bias) # (B, H)
839
+ trap = torch.sigmoid(torch.randn(batch, nheads, device=device, dtype=torch.float32))
840
+ xproj = torch.randn(mimo, nheads, hdim, device=device, dtype=dtype)
841
+ zproj = torch.randn(mimo, nheads, hdim, device=device, dtype=dtype)
842
+ outproj = torch.randn(mimo, nheads, hdim, device=device, dtype=dtype)
843
+ out = torch.zeros_like(x)
844
+
845
+ # =========================================================================
846
+ # Test 1: Out-of-place (explicit state_out)
847
+ # =========================================================================
848
+ print("=== Out-of-place test ===")
849
+ state_out = torch.zeros_like(state)
850
+ fn_oop = lambda: mamba3_step_fn(
851
+ state,
852
+ Bstate,
853
+ Xstate,
854
+ A,
855
+ B,
856
+ C,
857
+ D,
858
+ x,
859
+ dt,
860
+ trap,
861
+ xproj,
862
+ outproj,
863
+ state_out,
864
+ out,
865
+ z=z,
866
+ zproj=zproj,
867
+ tile_D=64,
868
+ num_warps=4,
869
+ )
870
+
871
+ fn_oop()
872
+ out_ref, state_out_ref = selective_state_update_fused_ref_v2(state, A, B, C, xproj, x, zproj, z, dt, Bstate, Xstate, trap, D, outproj, compute_dtype=torch.float64)
873
+ out_pt, state_out_pt = selective_state_update_fused_ref_v2(state, A, B, C, xproj, x, zproj, z, dt, Bstate, Xstate, trap, D, outproj, compute_dtype=torch.float32)
874
+ print(f"state_out vs ref (f64): {(state_out - state_out_ref).abs().max()}")
875
+ print(f"state_out_pt vs ref (f64): {(state_out_pt - state_out_ref).abs().max()}")
876
+ print(f"out vs ref (f64): {(out - out_ref).abs().max()}")
877
+ print(f"out_pt vs ref (f64): {(out_pt - out_ref).abs().max()}")
878
+
879
+ # =========================================================================
880
+ # Test 2: In-place (state_out=None)
881
+ # =========================================================================
882
+ print("\n=== In-place test ===")
883
+ # Fresh state for in-place test
884
+ state_ip = state.clone()
885
+ out_ip = torch.zeros_like(x)
886
+ fn_ip = lambda: mamba3_step_fn(
887
+ state_ip,
888
+ Bstate,
889
+ Xstate,
890
+ A,
891
+ B,
892
+ C,
893
+ D,
894
+ x,
895
+ dt,
896
+ trap,
897
+ xproj,
898
+ outproj,
899
+ None, # state_out=None -> in-place
900
+ out_ip,
901
+ z=z,
902
+ zproj=zproj,
903
+ tile_D=64,
904
+ num_warps=4,
905
+ )
906
+
907
+ fn_ip()
908
+ # state_ip was updated in place, compare against same reference
909
+ print(f"state (in-place) vs ref (f64): {(state_ip - state_out_ref).abs().max()}")
910
+ print(f"out (in-place) vs ref (f64): {(out_ip - out_ref).abs().max()}")
911
+ # Verify in-place and out-of-place produce identical results
912
+ print(f"state in-place vs out-of-place: {(state_ip - state_out).abs().max()}")
913
+ print(f"out in-place vs out-of-place: {(out_ip - out).abs().max()}")
914
+
915
+ # =========================================================================
916
+ # Benchmark (out-of-place)
917
+ # =========================================================================
918
+ read_bytes = (
919
+ _bytes_of(state) + _bytes_of(A) + _bytes_of(B)
920
+ + _bytes_of(C)
921
+ + _bytes_of(xproj) + _bytes_of(x)
922
+ + _bytes_of(zproj) + _bytes_of(z)
923
+ + _bytes_of(dt) + _bytes_of(Bstate) + _bytes_of(Xstate)
924
+ + _bytes_of(trap) + _bytes_of(D) + _bytes_of(outproj)
925
+ )
926
+ out_bytes = _bytes_of(out)
927
+ new_state_bytes = _bytes_of(state)
928
+ total_bytes = read_bytes + out_bytes + new_state_bytes
929
+
930
+ from triton.testing import do_bench_cudagraph
931
+ ms = do_bench_cudagraph(fn_oop, rep=30)
932
+ bandwidth = (total_bytes) / ms * 1e-6
933
+ print(f"\nMamba3 step (out-of-place): {ms:.3f} ms, {bandwidth:.1f} GB/s")
vendor/mamba-og/mamba_ssm/ops/selective_scan_interface.py ADDED
@@ -0,0 +1,419 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2023, Tri Dao, Albert Gu.
2
+
3
+ import torch
4
+ import torch.nn.functional as F
5
+ from mamba_ssm.utils.torch import custom_bwd, custom_fwd
6
+
7
+ from einops import rearrange, repeat
8
+
9
+ try:
10
+ from causal_conv1d import causal_conv1d_fn
11
+ from causal_conv1d.cpp_functions import causal_conv1d_fwd_function, causal_conv1d_bwd_function, causal_conv1d_update_function
12
+ except ImportError:
13
+ causal_conv1d_fn = None
14
+ causal_conv1d_fwd_function = None
15
+ causal_conv1d_bwd_function = None
16
+ causal_conv1d_update_function = None
17
+
18
+ from mamba_ssm.ops.triton.layer_norm import _layer_norm_fwd
19
+
20
+ import selective_scan_cuda
21
+
22
+
23
+ class SelectiveScanFn(torch.autograd.Function):
24
+
25
+ @staticmethod
26
+ def forward(ctx, u, delta, A, B, C, D=None, z=None, delta_bias=None, delta_softplus=False,
27
+ return_last_state=False):
28
+ if u.stride(-1) != 1:
29
+ u = u.contiguous()
30
+ if delta.stride(-1) != 1:
31
+ delta = delta.contiguous()
32
+ if D is not None:
33
+ D = D.contiguous()
34
+ if B.stride(-1) != 1:
35
+ B = B.contiguous()
36
+ if C.stride(-1) != 1:
37
+ C = C.contiguous()
38
+ if z is not None and z.stride(-1) != 1:
39
+ z = z.contiguous()
40
+ if B.dim() == 3:
41
+ B = rearrange(B, "b dstate l -> b 1 dstate l")
42
+ ctx.squeeze_B = True
43
+ if C.dim() == 3:
44
+ C = rearrange(C, "b dstate l -> b 1 dstate l")
45
+ ctx.squeeze_C = True
46
+ out, x, *rest = selective_scan_cuda.fwd(u, delta, A, B, C, D, z, delta_bias, delta_softplus)
47
+ ctx.delta_softplus = delta_softplus
48
+ ctx.has_z = z is not None
49
+ last_state = x[:, :, -1, 1::2] # (batch, dim, dstate)
50
+ if not ctx.has_z:
51
+ ctx.save_for_backward(u, delta, A, B, C, D, delta_bias, x)
52
+ return out if not return_last_state else (out, last_state)
53
+ else:
54
+ ctx.save_for_backward(u, delta, A, B, C, D, z, delta_bias, x, out)
55
+ out_z = rest[0]
56
+ return out_z if not return_last_state else (out_z, last_state)
57
+
58
+ @staticmethod
59
+ def backward(ctx, dout, *args):
60
+ if not ctx.has_z:
61
+ u, delta, A, B, C, D, delta_bias, x = ctx.saved_tensors
62
+ z = None
63
+ out = None
64
+ else:
65
+ u, delta, A, B, C, D, z, delta_bias, x, out = ctx.saved_tensors
66
+ if dout.stride(-1) != 1:
67
+ dout = dout.contiguous()
68
+ # The kernel supports passing in a pre-allocated dz (e.g., in case we want to fuse the
69
+ # backward of selective_scan_cuda with the backward of chunk).
70
+ # Here we just pass in None and dz will be allocated in the C++ code.
71
+ du, ddelta, dA, dB, dC, dD, ddelta_bias, *rest = selective_scan_cuda.bwd(
72
+ u, delta, A, B, C, D, z, delta_bias, dout, x, out, None, ctx.delta_softplus,
73
+ False # option to recompute out_z, not used here
74
+ )
75
+ dz = rest[0] if ctx.has_z else None
76
+ dB = dB.squeeze(1) if getattr(ctx, "squeeze_B", False) else dB
77
+ dC = dC.squeeze(1) if getattr(ctx, "squeeze_C", False) else dC
78
+ return (du, ddelta, dA, dB, dC,
79
+ dD if D is not None else None,
80
+ dz,
81
+ ddelta_bias if delta_bias is not None else None,
82
+ None,
83
+ None)
84
+
85
+
86
+ def rms_norm_forward(
87
+ x,
88
+ weight,
89
+ bias,
90
+ eps=1e-6,
91
+ is_rms_norm=True,
92
+ ):
93
+ # x (b l) d
94
+ if x.stride(-1) != 1:
95
+ x = x.contiguous()
96
+ weight = weight.contiguous()
97
+ if bias is not None:
98
+ bias = bias.contiguous()
99
+ y = _layer_norm_fwd(
100
+ x, weight, bias, eps, None, residual_dtype=None, is_rms_norm=is_rms_norm
101
+ )[0]
102
+ # y (b l) d
103
+ return y
104
+
105
+
106
+ def selective_scan_fn(u, delta, A, B, C, D=None, z=None, delta_bias=None, delta_softplus=False,
107
+ return_last_state=False):
108
+ """if return_last_state is True, returns (out, last_state)
109
+ last_state has shape (batch, dim, dstate). Note that the gradient of the last state is
110
+ not considered in the backward pass.
111
+ """
112
+ return SelectiveScanFn.apply(u, delta, A, B, C, D, z, delta_bias, delta_softplus, return_last_state)
113
+
114
+
115
+ def selective_scan_ref(u, delta, A, B, C, D=None, z=None, delta_bias=None, delta_softplus=False,
116
+ return_last_state=False):
117
+ """
118
+ u: r(B D L)
119
+ delta: r(B D L)
120
+ A: c(D N) or r(D N)
121
+ B: c(D N) or r(B N L) or r(B N 2L) or r(B G N L) or (B G N L)
122
+ C: c(D N) or r(B N L) or r(B N 2L) or r(B G N L) or (B G N L)
123
+ D: r(D)
124
+ z: r(B D L)
125
+ delta_bias: r(D), fp32
126
+
127
+ out: r(B D L)
128
+ last_state (optional): r(B D dstate) or c(B D dstate)
129
+ """
130
+ dtype_in = u.dtype
131
+ u = u.float()
132
+ delta = delta.float()
133
+ if delta_bias is not None:
134
+ delta = delta + delta_bias[..., None].float()
135
+ if delta_softplus:
136
+ delta = F.softplus(delta)
137
+ batch, dim, dstate = u.shape[0], A.shape[0], A.shape[1]
138
+ is_variable_B = B.dim() >= 3
139
+ is_variable_C = C.dim() >= 3
140
+ if A.is_complex():
141
+ if is_variable_B:
142
+ B = torch.view_as_complex(rearrange(B.float(), "... (L two) -> ... L two", two=2))
143
+ if is_variable_C:
144
+ C = torch.view_as_complex(rearrange(C.float(), "... (L two) -> ... L two", two=2))
145
+ else:
146
+ B = B.float()
147
+ C = C.float()
148
+ x = A.new_zeros((batch, dim, dstate))
149
+ ys = []
150
+ deltaA = torch.exp(torch.einsum('bdl,dn->bdln', delta, A))
151
+ if not is_variable_B:
152
+ deltaB_u = torch.einsum('bdl,dn,bdl->bdln', delta, B, u)
153
+ else:
154
+ if B.dim() == 3:
155
+ deltaB_u = torch.einsum('bdl,bnl,bdl->bdln', delta, B, u)
156
+ else:
157
+ B = repeat(B, "B G N L -> B (G H) N L", H=dim // B.shape[1])
158
+ deltaB_u = torch.einsum('bdl,bdnl,bdl->bdln', delta, B, u)
159
+ if is_variable_C and C.dim() == 4:
160
+ C = repeat(C, "B G N L -> B (G H) N L", H=dim // C.shape[1])
161
+ last_state = None
162
+ for i in range(u.shape[2]):
163
+ x = deltaA[:, :, i] * x + deltaB_u[:, :, i]
164
+ if not is_variable_C:
165
+ y = torch.einsum('bdn,dn->bd', x, C)
166
+ else:
167
+ if C.dim() == 3:
168
+ y = torch.einsum('bdn,bn->bd', x, C[:, :, i])
169
+ else:
170
+ y = torch.einsum('bdn,bdn->bd', x, C[:, :, :, i])
171
+ if i == u.shape[2] - 1:
172
+ last_state = x
173
+ if y.is_complex():
174
+ y = y.real * 2
175
+ ys.append(y)
176
+ y = torch.stack(ys, dim=2) # (batch dim L)
177
+ out = y if D is None else y + u * rearrange(D, "d -> d 1")
178
+ if z is not None:
179
+ out = out * F.silu(z)
180
+ out = out.to(dtype=dtype_in)
181
+ return out if not return_last_state else (out, last_state)
182
+
183
+
184
+ class MambaInnerFn(torch.autograd.Function):
185
+
186
+ @staticmethod
187
+ @custom_fwd
188
+ def forward(ctx, xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
189
+ out_proj_weight, out_proj_bias,
190
+ A, B=None, C=None, D=None, delta_bias=None, B_proj_bias=None,
191
+ C_proj_bias=None, delta_softplus=True, checkpoint_lvl=1, b_rms_weight=None, c_rms_weight= None, dt_rms_weight= None, b_c_dt_rms_eps=1e-6):
192
+ """
193
+ xz: (batch, dim, seqlen)
194
+ """
195
+ assert causal_conv1d_fwd_function is not None, "causal_conv1d_cuda is not available. Please install causal-conv1d."
196
+ assert checkpoint_lvl in [0, 1]
197
+ L = xz.shape[-1]
198
+ delta_rank = delta_proj_weight.shape[1]
199
+ d_state = A.shape[-1] * (1 if not A.is_complex() else 2)
200
+ if torch.is_autocast_enabled():
201
+ x_proj_weight = x_proj_weight.to(dtype=torch.get_autocast_gpu_dtype())
202
+ delta_proj_weight = delta_proj_weight.to(dtype=torch.get_autocast_gpu_dtype())
203
+ out_proj_weight = out_proj_weight.to(dtype=torch.get_autocast_gpu_dtype())
204
+ out_proj_bias = (out_proj_bias.to(dtype=torch.get_autocast_gpu_dtype())
205
+ if out_proj_bias is not None else None)
206
+ if xz.stride(-1) != 1:
207
+ xz = xz.contiguous()
208
+ conv1d_weight = rearrange(conv1d_weight, "d 1 w -> d w")
209
+ x, z = xz.chunk(2, dim=1)
210
+ conv1d_bias = conv1d_bias.contiguous() if conv1d_bias is not None else None
211
+ conv1d_out = causal_conv1d_fwd_function(
212
+ x, conv1d_weight, conv1d_bias, None, None, None, True
213
+ )
214
+ # We're being very careful here about the layout, to avoid extra transposes.
215
+ # We want delta to have d as the slowest moving dimension
216
+ # and L as the fastest moving dimension, since those are what the ssm_scan kernel expects.
217
+ x_dbl = F.linear(rearrange(conv1d_out, 'b d l -> (b l) d'), x_proj_weight) # (bl d)
218
+ delta = rearrange(delta_proj_weight @ x_dbl[:, :delta_rank].t(), "d (b l) -> b d l", l = L)
219
+ ctx.is_variable_B = B is None
220
+ ctx.is_variable_C = C is None
221
+ ctx.B_proj_bias_is_None = B_proj_bias is None
222
+ ctx.C_proj_bias_is_None = C_proj_bias is None
223
+ if B is None: # variable B
224
+ B = x_dbl[:, delta_rank:delta_rank + d_state] # (bl dstate)
225
+ if B_proj_bias is not None:
226
+ B = B + B_proj_bias.to(dtype=B.dtype)
227
+ if not A.is_complex():
228
+ # B = rearrange(B, "(b l) dstate -> b dstate l", l=L).contiguous()
229
+ B = rearrange(B, "(b l) dstate -> b 1 dstate l", l=L).contiguous()
230
+ else:
231
+ B = rearrange(B, "(b l) (dstate two) -> b 1 dstate (l two)", l=L, two=2).contiguous()
232
+ else:
233
+ if B.stride(-1) != 1:
234
+ B = B.contiguous()
235
+ if C is None: # variable C
236
+ C = x_dbl[:, -d_state:] # (bl dstate)
237
+ if C_proj_bias is not None:
238
+ C = C + C_proj_bias.to(dtype=C.dtype)
239
+ if not A.is_complex():
240
+ # C = rearrange(C, "(b l) dstate -> b dstate l", l=L).contiguous()
241
+ C = rearrange(C, "(b l) dstate -> b 1 dstate l", l=L).contiguous()
242
+ else:
243
+ C = rearrange(C, "(b l) (dstate two) -> b 1 dstate (l two)", l=L, two=2).contiguous()
244
+ else:
245
+ if C.stride(-1) != 1:
246
+ C = C.contiguous()
247
+ if D is not None:
248
+ D = D.contiguous()
249
+
250
+ if b_rms_weight is not None:
251
+ B = rearrange(B, "b 1 dstate l -> (b l) dstate", l=L).contiguous()
252
+ B = rms_norm_forward(B, b_rms_weight, bias=None, eps=b_c_dt_rms_eps)
253
+ B = rearrange(B, "(b l) dstate -> b 1 dstate l", l=L).contiguous()
254
+ if c_rms_weight is not None:
255
+ C = rearrange(C, "b 1 dstate l -> (b l) dstate", l=L).contiguous()
256
+ C = rms_norm_forward(C, c_rms_weight, bias=None, eps=b_c_dt_rms_eps)
257
+ C = rearrange(C, "(b l) dstate -> b 1 dstate l", l=L).contiguous()
258
+ if dt_rms_weight is not None:
259
+ delta = rearrange(delta, "b d l -> (b l) d", l=L).contiguous()
260
+ delta = rms_norm_forward(delta, dt_rms_weight, bias=None, eps=b_c_dt_rms_eps)
261
+ delta = rearrange(delta, "(b l) d -> b d l", l=L).contiguous()
262
+
263
+ out, scan_intermediates, out_z = selective_scan_cuda.fwd(
264
+ conv1d_out, delta, A, B, C, D, z, delta_bias, delta_softplus
265
+ )
266
+ ctx.delta_softplus = delta_softplus
267
+ ctx.out_proj_bias_is_None = out_proj_bias is None
268
+ ctx.checkpoint_lvl = checkpoint_lvl
269
+ ctx.b_rms_weight = b_rms_weight
270
+ ctx.c_rms_weight = c_rms_weight
271
+ ctx.dt_rms_weight = dt_rms_weight
272
+ ctx.b_c_dt_rms_eps = b_c_dt_rms_eps
273
+ if checkpoint_lvl >= 1: # Will recompute conv1d_out and delta in the backward pass
274
+ conv1d_out, delta = None, None
275
+ ctx.save_for_backward(xz, conv1d_weight, conv1d_bias, x_dbl, x_proj_weight,
276
+ delta_proj_weight, out_proj_weight, conv1d_out, delta,
277
+ A, B, C, D, delta_bias, scan_intermediates, b_rms_weight, c_rms_weight, dt_rms_weight, out)
278
+ return F.linear(rearrange(out_z, "b d l -> b l d"), out_proj_weight, out_proj_bias)
279
+
280
+ @staticmethod
281
+ @custom_bwd
282
+ def backward(ctx, dout):
283
+ # dout: (batch, seqlen, dim)
284
+ assert causal_conv1d_fwd_function is not None, "causal_conv1d_cuda is not available. Please install causal-conv1d."
285
+ (xz, conv1d_weight, conv1d_bias, x_dbl, x_proj_weight, delta_proj_weight, out_proj_weight,
286
+ conv1d_out, delta, A, B, C, D, delta_bias, scan_intermediates, b_rms_weight, c_rms_weight, dt_rms_weight, out) = ctx.saved_tensors
287
+ L = xz.shape[-1]
288
+ delta_rank = delta_proj_weight.shape[1]
289
+ d_state = A.shape[-1] * (1 if not A.is_complex() else 2)
290
+ x, z = xz.chunk(2, dim=1)
291
+ if dout.stride(-1) != 1:
292
+ dout = dout.contiguous()
293
+ if ctx.checkpoint_lvl == 1:
294
+ conv1d_out = causal_conv1d_fwd_function(
295
+ x, conv1d_weight, conv1d_bias, None, None, None, True
296
+ )
297
+ delta = rearrange(delta_proj_weight @ x_dbl[:, :delta_rank].t(),
298
+ "d (b l) -> b d l", l = L)
299
+ if dt_rms_weight is not None:
300
+ delta = rearrange(delta, "b d l -> (b l) d", l=L).contiguous()
301
+ delta = rms_norm_forward(delta, ctx.dt_rms_weight, None, ctx.b_c_dt_rms_eps)
302
+ delta = rearrange(delta, "(b l) d -> b d l", l=L).contiguous()
303
+ if b_rms_weight is not None:
304
+ # Recompute & RMSNorm B
305
+ B = rearrange(B, "b 1 dstate l -> (b l) dstate", l=L).contiguous()
306
+ B = rms_norm_forward(
307
+ B, ctx.b_rms_weight, None, ctx.b_c_dt_rms_eps
308
+ )
309
+ B = rearrange(B, "(b l) dstate -> b 1 dstate l", l=L).contiguous()
310
+ if c_rms_weight is not None:
311
+ # Recompute & RMSNorm C
312
+ C = rearrange(C, "b 1 dstate l -> (b l) dstate", l=L).contiguous()
313
+ C = rms_norm_forward(
314
+ C, ctx.c_rms_weight, None, ctx.b_c_dt_rms_eps
315
+ )
316
+ C = rearrange(C, "(b l) dstate -> b 1 dstate l", l=L).contiguous()
317
+
318
+ # The kernel supports passing in a pre-allocated dz (e.g., in case we want to fuse the
319
+ # backward of selective_scan_cuda with the backward of chunk).
320
+ dxz = torch.empty_like(xz) # (batch, dim, seqlen)
321
+ dx, dz = dxz.chunk(2, dim=1)
322
+ dout = rearrange(dout, "b l e -> e (b l)")
323
+ dout_y = rearrange(out_proj_weight.t() @ dout, "d (b l) -> b d l", l=L)
324
+ dconv1d_out, ddelta, dA, dB, dC, dD, ddelta_bias, dz, out_z = selective_scan_cuda.bwd(
325
+ conv1d_out, delta, A, B, C, D, z, delta_bias, dout_y, scan_intermediates, out, dz,
326
+ ctx.delta_softplus,
327
+ True # option to recompute out_z
328
+ )
329
+ dout_proj_weight = torch.einsum("eB,dB->ed", dout, rearrange(out_z, "b d l -> d (b l)"))
330
+ dout_proj_bias = dout.sum(dim=(0, 1)) if not ctx.out_proj_bias_is_None else None
331
+ dD = dD if D is not None else None
332
+ dx_dbl = torch.empty_like(x_dbl)
333
+ dB_proj_bias = None
334
+ if ctx.is_variable_B:
335
+ if not A.is_complex():
336
+ dB = rearrange(dB, "b 1 dstate l -> (b l) dstate").contiguous()
337
+ else:
338
+ dB = rearrange(dB, "b 1 dstate (l two) -> (b l) (dstate two)", two=2).contiguous()
339
+ dB_proj_bias = dB.sum(0) if not ctx.B_proj_bias_is_None else None
340
+ dx_dbl[:, delta_rank:delta_rank + d_state] = dB # (bl d)
341
+ dB = None
342
+ dC_proj_bias = None
343
+ if ctx.is_variable_C:
344
+ if not A.is_complex():
345
+ dC = rearrange(dC, "b 1 dstate l -> (b l) dstate").contiguous()
346
+ else:
347
+ dC = rearrange(dC, "b 1 dstate (l two) -> (b l) (dstate two)", two=2).contiguous()
348
+ dC_proj_bias = dC.sum(0) if not ctx.C_proj_bias_is_None else None
349
+ dx_dbl[:, -d_state:] = dC # (bl d)
350
+ dC = None
351
+ ddelta = rearrange(ddelta, "b d l -> d (b l)")
352
+ ddelta_proj_weight = torch.einsum("dB,Br->dr", ddelta, x_dbl[:, :delta_rank])
353
+ dx_dbl[:, :delta_rank] = torch.einsum("dB,dr->Br", ddelta, delta_proj_weight)
354
+ dconv1d_out = rearrange(dconv1d_out, "b d l -> d (b l)")
355
+ dx_proj_weight = torch.einsum("Br,Bd->rd", dx_dbl, rearrange(conv1d_out, "b d l -> (b l) d"))
356
+ dconv1d_out = torch.addmm(dconv1d_out, x_proj_weight.t(), dx_dbl.t(), out=dconv1d_out)
357
+ dconv1d_out = rearrange(dconv1d_out, "d (b l) -> b d l", b=x.shape[0], l=x.shape[-1])
358
+ # The kernel supports passing in a pre-allocated dx (e.g., in case we want to fuse the
359
+ # backward of conv1d with the backward of chunk).
360
+ dx, dconv1d_weight, dconv1d_bias, *_ = causal_conv1d_bwd_function(
361
+ x, conv1d_weight, conv1d_bias, dconv1d_out, None, None, None, dx, False, True
362
+ )
363
+ dconv1d_bias = dconv1d_bias if conv1d_bias is not None else None
364
+ dconv1d_weight = rearrange(dconv1d_weight, "d w -> d 1 w")
365
+ return (dxz, dconv1d_weight, dconv1d_bias, dx_proj_weight, ddelta_proj_weight,
366
+ dout_proj_weight, dout_proj_bias,
367
+ dA, dB, dC, dD,
368
+ ddelta_bias if delta_bias is not None else None,
369
+ # 6-None are delta_softplus, checkpoint_lvl, b_rms_weight, c_rms_weight, dt_rms_weight, b_c_dt_rms_eps
370
+ dB_proj_bias, dC_proj_bias, None, None, None, None, None, None)
371
+
372
+
373
+ def mamba_inner_fn(
374
+ xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
375
+ out_proj_weight, out_proj_bias,
376
+ A, B=None, C=None, D=None, delta_bias=None, B_proj_bias=None,
377
+ C_proj_bias=None, delta_softplus=True, checkpoint_lvl=1, b_rms_weight= None, c_rms_weight= None, dt_rms_weight= None, b_c_dt_rms_eps=1e-6
378
+ ):
379
+ return MambaInnerFn.apply(xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
380
+ out_proj_weight, out_proj_bias,
381
+ A, B, C, D, delta_bias, B_proj_bias, C_proj_bias, delta_softplus, checkpoint_lvl, b_rms_weight, c_rms_weight, dt_rms_weight, b_c_dt_rms_eps)
382
+
383
+
384
+ def mamba_inner_ref(
385
+ xz, conv1d_weight, conv1d_bias, x_proj_weight, delta_proj_weight,
386
+ out_proj_weight, out_proj_bias,
387
+ A, B=None, C=None, D=None, delta_bias=None, B_proj_bias=None,
388
+ C_proj_bias=None, delta_softplus=True
389
+ ):
390
+ assert causal_conv1d_fn is not None, "causal_conv1d_fn is not available. Please install causal-conv1d."
391
+ L = xz.shape[-1]
392
+ delta_rank = delta_proj_weight.shape[1]
393
+ d_state = A.shape[-1] * (1 if not A.is_complex() else 2)
394
+ x, z = xz.chunk(2, dim=1)
395
+ x = causal_conv1d_fn(x, rearrange(conv1d_weight, "d 1 w -> d w"), conv1d_bias, activation="silu")
396
+ # We're being very careful here about the layout, to avoid extra transposes.
397
+ # We want delta to have d as the slowest moving dimension
398
+ # and L as the fastest moving dimension, since those are what the ssm_scan kernel expects.
399
+ x_dbl = F.linear(rearrange(x, 'b d l -> (b l) d'), x_proj_weight) # (bl d)
400
+ delta = delta_proj_weight @ x_dbl[:, :delta_rank].t()
401
+ delta = rearrange(delta, "d (b l) -> b d l", l=L)
402
+ if B is None: # variable B
403
+ B = x_dbl[:, delta_rank:delta_rank + d_state] # (bl d)
404
+ if B_proj_bias is not None:
405
+ B = B + B_proj_bias.to(dtype=B.dtype)
406
+ if not A.is_complex():
407
+ B = rearrange(B, "(b l) dstate -> b dstate l", l=L).contiguous()
408
+ else:
409
+ B = rearrange(B, "(b l) (dstate two) -> b dstate (l two)", l=L, two=2).contiguous()
410
+ if C is None: # variable B
411
+ C = x_dbl[:, -d_state:] # (bl d)
412
+ if C_proj_bias is not None:
413
+ C = C + C_proj_bias.to(dtype=C.dtype)
414
+ if not A.is_complex():
415
+ C = rearrange(C, "(b l) dstate -> b dstate l", l=L).contiguous()
416
+ else:
417
+ C = rearrange(C, "(b l) (dstate two) -> b dstate (l two)", l=L, two=2).contiguous()
418
+ y = selective_scan_fn(x, delta, A, B, C, D, z=z, delta_bias=delta_bias, delta_softplus=True)
419
+ return F.linear(rearrange(y, "b d l -> b l d"), out_proj_weight, out_proj_bias)