A vendor source
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +3 -0
- vendor/mamba-og/.gitignore +6 -0
- vendor/mamba-og/.gitmodules +3 -0
- vendor/mamba-og/AUTHORS +2 -0
- vendor/mamba-og/LICENSE +201 -0
- vendor/mamba-og/MANIFEST.in +3 -0
- vendor/mamba-og/README.md +282 -0
- vendor/mamba-og/assets/mamba3.png +3 -0
- vendor/mamba-og/assets/selection.png +3 -0
- vendor/mamba-og/assets/ssd_algorithm.png +3 -0
- vendor/mamba-og/benchmarks/benchmark_generation_mamba_simple.py +92 -0
- vendor/mamba-og/csrc/selective_scan/reverse_scan.cuh +452 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan.cpp +497 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan.h +82 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_bf16_complex.cu +9 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_bf16_real.cu +9 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_fp16_complex.cu +9 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_fp16_real.cu +9 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_fp32_complex.cu +9 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_fp32_real.cu +9 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan_bwd_kernel.cuh +561 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan_common.h +255 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan_fwd_bf16.cu +10 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan_fwd_fp16.cu +10 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan_fwd_fp32.cu +10 -0
- vendor/mamba-og/csrc/selective_scan/selective_scan_fwd_kernel.cuh +376 -0
- vendor/mamba-og/csrc/selective_scan/static_switch.h +25 -0
- vendor/mamba-og/csrc/selective_scan/uninitialized_copy.cuh +77 -0
- vendor/mamba-og/evals/lm_harness_eval.py +39 -0
- vendor/mamba-og/mamba_ssm/__init__.py +24 -0
- vendor/mamba-og/mamba_ssm/distributed/__init__.py +0 -0
- vendor/mamba-og/mamba_ssm/distributed/distributed_utils.py +144 -0
- vendor/mamba-og/mamba_ssm/distributed/tensor_parallel.py +296 -0
- vendor/mamba-og/mamba_ssm/models/__init__.py +0 -0
- vendor/mamba-og/mamba_ssm/models/config_mamba.py +18 -0
- vendor/mamba-og/mamba_ssm/models/mixer_seq_simple.py +309 -0
- vendor/mamba-og/mamba_ssm/modules/__init__.py +0 -0
- vendor/mamba-og/mamba_ssm/modules/block.py +91 -0
- vendor/mamba-og/mamba_ssm/modules/mamba2.py +383 -0
- vendor/mamba-og/mamba_ssm/modules/mamba2_simple.py +200 -0
- vendor/mamba-og/mamba_ssm/modules/mamba3.py +498 -0
- vendor/mamba-og/mamba_ssm/modules/mamba_simple.py +294 -0
- vendor/mamba-og/mamba_ssm/modules/mha.py +294 -0
- vendor/mamba-og/mamba_ssm/modules/mlp.py +34 -0
- vendor/mamba-og/mamba_ssm/modules/ssd_minimal.py +103 -0
- vendor/mamba-og/mamba_ssm/ops/__init__.py +0 -0
- vendor/mamba-og/mamba_ssm/ops/cute/__init__.py +0 -0
- vendor/mamba-og/mamba_ssm/ops/cute/mamba3/__init__.py +0 -0
- vendor/mamba-og/mamba_ssm/ops/cute/mamba3/mamba3_step_fn.py +933 -0
- 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 |
+

|
| 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 |
+

|
| 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 |
+

|
| 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
|
vendor/mamba-og/assets/selection.png
ADDED
|
Git LFS Details
|
vendor/mamba-og/assets/ssd_algorithm.png
ADDED
|
Git LFS Details
|
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 ¶ms, cudaStream_t stream);
|
| 55 |
+
|
| 56 |
+
template <typename input_t, typename weight_t>
|
| 57 |
+
void selective_scan_bwd_cuda(SSMParamsBwd ¶ms, cudaStream_t stream);
|
| 58 |
+
|
| 59 |
+
void set_ssm_params_fwd(SSMParamsBase ¶ms,
|
| 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(¶ms, 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 ¶ms,
|
| 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, cudaStream_t stream);
|
| 10 |
+
template void selective_scan_fwd_cuda<at::BFloat16, complex_t>(SSMParamsBase ¶ms, 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 ¶ms, cudaStream_t stream);
|
| 10 |
+
template void selective_scan_fwd_cuda<at::Half, complex_t>(SSMParamsBase ¶ms, 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 ¶ms, cudaStream_t stream);
|
| 10 |
+
template void selective_scan_fwd_cuda<float, complex_t>(SSMParamsBase ¶ms, 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 ¶ms, 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 ¶ms, 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)
|