| #include <torch/library.h> |
| #include <c10/cuda/CUDAStream.h> |
| #include <c10/cuda/CUDAException.h> |
| #include <algorithm> |
|
|
| #include "torch_binding.h" |
|
|
| |
| #define M3_F32(x) TORCH_CHECK((x).is_cuda() && (x).scalar_type() == at::kFloat && (x).is_contiguous(), \ |
| #x " must be a contiguous float32 CUDA tensor") |
|
|
| #define STEP_ARGS \ |
| const void* q, const void* k, const void* v, const void* z, const float* adt, \ |
| const float* dt, const float* trap, const void* q_bias, const void* k_bias, \ |
| const float* angles, const void* mimo_v, const void* mimo_o, const void* mimo_z, \ |
| const void* Dvec, const float* angle_in, float* angle_out, float* S_st, \ |
| const float* kprev_in, const float* vprev_in, float* kprev_out, float* vprev_out, \ |
| void* y, int B, int H, int Gqk, int R, int N, int P, int Na, void* stream |
|
|
| void mamba3_mimo_step_launch_f32(STEP_ARGS); |
| void mamba3_mimo_step_launch_bf16(STEP_ARGS); |
|
|
| static const void* vptr(const c10::optional<at::Tensor>& t, at::ScalarType st) { |
| if (!t.has_value() || !t->defined()) return nullptr; |
| TORCH_CHECK(t->is_cuda() && t->scalar_type() == st && t->is_contiguous(), |
| "optional tensors must be contiguous CUDA tensors of the input dtype"); |
| return t->data_ptr(); |
| } |
|
|
| void mamba3_cumulative_angles_launch( |
| const float* angles, const float* dt, float* out, float* seg_tot, |
| int B, int S, int H, int Na, int nseg, void* stream); |
| int mamba3_cumulative_angles_segments(int B, int S, int H); |
|
|
| torch::Tensor mamba3_cumulative_angles(torch::Tensor angles, torch::Tensor dt) |
| { |
| M3_F32(angles); M3_F32(dt); |
| TORCH_CHECK(angles.dim() == 4 && dt.dim() == 3, "angles is (B,S,H,Na), dt is (B,H,S)"); |
| const int B = (int)angles.size(0), S = (int)angles.size(1); |
| const int H = (int)angles.size(2), Na = (int)angles.size(3); |
| TORCH_CHECK(dt.size(0) == B && dt.size(1) == H && dt.size(2) == S, |
| "dt must be (B,H,S) matching angles"); |
| auto out = torch::empty_like(angles); |
| const int nseg = mamba3_cumulative_angles_segments(B, S, H); |
| auto seg_tot = torch::empty({B * H, nseg, Na}, angles.options()); |
| mamba3_cumulative_angles_launch( |
| angles.data_ptr<float>(), dt.data_ptr<float>(), out.data_ptr<float>(), |
| seg_tot.data_ptr<float>(), B, S, H, Na, nseg, |
| c10::cuda::getCurrentCUDAStream().stream()); |
| C10_CUDA_KERNEL_LAUNCH_CHECK(); |
| return out; |
| } |
|
|
| |
| |
| |
| std::vector<int64_t> mamba3_fwd_dispatch( |
| int64_t mimo_rank, int64_t dstate, int64_t headdim, int64_t chunk_size, bool bf16) |
| { |
| TORCH_CHECK(chunk_size > 0, "chunk_size must be positive"); |
| int state = 0, scan = 0, tf32 = 0; |
| mamba3_fwd_dispatch_paths((int)mimo_rank, (int)dstate, (int)headdim, |
| (int)chunk_size, bf16 ? 1 : 0, &state, &scan, &tf32); |
| return {state, scan, tf32}; |
| } |
|
|
| torch::Tensor mamba3_mimo_step( |
| torch::Tensor q, torch::Tensor k, torch::Tensor v, c10::optional<at::Tensor> z, |
| torch::Tensor adt, torch::Tensor dt, torch::Tensor trap, |
| torch::Tensor q_bias, torch::Tensor k_bias, torch::Tensor angles, |
| torch::Tensor mimo_v, torch::Tensor mimo_o, |
| c10::optional<at::Tensor> mimo_z, c10::optional<at::Tensor> D, |
| torch::Tensor angle_in, torch::Tensor angle_out, torch::Tensor S_st, |
| torch::Tensor kprev_in, torch::Tensor vprev_in, |
| torch::Tensor kprev_out, torch::Tensor vprev_out) |
| { |
| const auto sdt = q.scalar_type(); |
| TORCH_CHECK(sdt == at::kFloat || sdt == at::kBFloat16, |
| "inputs must be float32 or bfloat16"); |
| for (const auto& t : {q, k, v, q_bias, k_bias, mimo_v, mimo_o}) |
| TORCH_CHECK(t.is_cuda() && t.is_contiguous() && t.scalar_type() == sdt, |
| "all value inputs must be contiguous CUDA tensors of one dtype"); |
| M3_F32(adt); M3_F32(dt); M3_F32(trap); M3_F32(angles); |
| M3_F32(angle_in); M3_F32(angle_out); M3_F32(S_st); |
| M3_F32(kprev_in); M3_F32(vprev_in); M3_F32(kprev_out); M3_F32(vprev_out); |
|
|
| const int B = (int)q.size(0), R = (int)q.size(1), Gqk = (int)q.size(2), N = (int)q.size(3); |
| const int H = (int)v.size(1), P = (int)v.size(2), Na = (int)angles.size(2); |
|
|
| TORCH_CHECK(N % 2 == 0, "state dimension N must be even"); |
| TORCH_CHECK(H % Gqk == 0, "nheads must be divisible by the query-group count"); |
| TORCH_CHECK(Na <= N / 2, "angle count must not exceed N/2"); |
| TORCH_CHECK(R <= 8, "mimo_rank above 8 is not supported"); |
| TORCH_CHECK(S_st.size(2) == P && S_st.size(3) == N, "state shape does not match inputs"); |
|
|
| auto y = torch::empty({B, H, P}, v.options()); |
| auto fn = (sdt == at::kFloat) ? mamba3_mimo_step_launch_f32 : mamba3_mimo_step_launch_bf16; |
| fn(q.data_ptr(), k.data_ptr(), v.data_ptr(), vptr(z, sdt), |
| adt.data_ptr<float>(), dt.data_ptr<float>(), trap.data_ptr<float>(), |
| q_bias.data_ptr(), k_bias.data_ptr(), angles.data_ptr<float>(), |
| mimo_v.data_ptr(), mimo_o.data_ptr(), vptr(mimo_z, sdt), vptr(D, sdt), |
| angle_in.data_ptr<float>(), angle_out.data_ptr<float>(), S_st.data_ptr<float>(), |
| kprev_in.data_ptr<float>(), vprev_in.data_ptr<float>(), |
| kprev_out.data_ptr<float>(), vprev_out.data_ptr<float>(), y.data_ptr(), |
| B, H, Gqk, R, N, P, Na, c10::cuda::getCurrentCUDAStream().stream()); |
| C10_CUDA_KERNEL_LAUNCH_CHECK(); |
| return y; |
| } |
|
|
| #define FWD_ARGS \ |
| const void* q, const void* k, const void* v, const void* z, const void* q_bias, \ |
| const void* k_bias, const void* mimo_v, const void* mimo_o, const void* mimo_z, \ |
| const void* Dvec, const void* angles, const float* dt, const float* trap, \ |
| const float* dA_cs, const float* dA_cs_rev, const void* norm_w, float norm_eps, \ |
| int fused_norm, void* kv_ws, void* out, \ |
| int B, int S, int H, int Gqk, int R, int N, int P, int Na, int C, void* stream |
|
|
| void mamba3_mimo_fwd_launch_f32(FWD_ARGS); |
| void mamba3_mimo_fwd_launch_bf16(FWD_ARGS); |
|
|
| torch::Tensor mamba3_mimo_fwd( |
| torch::Tensor q, torch::Tensor k, torch::Tensor v, c10::optional<at::Tensor> z, |
| torch::Tensor q_bias, torch::Tensor k_bias, torch::Tensor mimo_v, torch::Tensor mimo_o, |
| c10::optional<at::Tensor> mimo_z, c10::optional<at::Tensor> D, |
| torch::Tensor angles, torch::Tensor dt, torch::Tensor trap, |
| torch::Tensor dA_cs, torch::Tensor dA_cs_rev, int64_t chunk_size, |
| c10::optional<at::Tensor> norm_weight, double norm_eps) |
| { |
| const auto sdt = q.scalar_type(); |
| TORCH_CHECK(sdt == at::kFloat || sdt == at::kBFloat16, |
| "inputs must be float32 or bfloat16"); |
| for (const auto& t : {q, k, v, q_bias, k_bias, mimo_v, mimo_o}) |
| TORCH_CHECK(t.is_cuda() && t.is_contiguous() && t.scalar_type() == sdt, |
| "all value inputs must be contiguous CUDA tensors of one dtype"); |
| |
| M3_F32(dt); M3_F32(trap); M3_F32(dA_cs); M3_F32(dA_cs_rev); M3_F32(angles); |
|
|
| const int B = (int)q.size(0), S = (int)q.size(1), R = (int)q.size(2); |
| const int Gqk = (int)q.size(3), N = (int)q.size(4); |
| const int H = (int)v.size(2), P = (int)v.size(3), Na = (int)angles.size(3); |
| const int C = (int)chunk_size; |
| TORCH_CHECK(C > 0, "chunk_size must be positive"); |
| const int Nc = (S + C - 1) / C; |
|
|
| TORCH_CHECK(N % 2 == 0, "state dimension N must be even"); |
| TORCH_CHECK(H % Gqk == 0, "nheads must be divisible by the query-group count"); |
| TORCH_CHECK(Na <= N / 2, "angle count must not exceed N/2"); |
| |
| |
| TORCH_CHECK(R <= 8, "mimo_rank above 8 is not supported"); |
|
|
| const size_t smem = mamba3_fwd_smem_bytes(R, N, P, C, sdt == at::kBFloat16 ? 1 : 0); |
| const size_t cap = (size_t)mamba3_max_smem_optin(); |
| TORCH_CHECK(smem <= cap, |
| "chunk_size ", C, " with mimo_rank ", R, ", headdim ", P, " and state ", N, |
| " needs ", smem / 1024, " KB of shared memory per block, above this " |
| "device's ", cap / 1024, " KB opt-in limit"); |
|
|
| const int fused = (norm_weight.has_value() && norm_weight->defined()) ? 1 : 0; |
| auto out = torch::empty({B, S, H, P}, v.options()); |
| |
| |
| |
| auto kv_ws = torch::empty({B, H, Nc, N, P}, v.options()); |
|
|
| auto fn = (sdt == at::kFloat) ? mamba3_mimo_fwd_launch_f32 : mamba3_mimo_fwd_launch_bf16; |
| fn(q.data_ptr(), k.data_ptr(), v.data_ptr(), vptr(z, sdt), q_bias.data_ptr(), |
| k_bias.data_ptr(), mimo_v.data_ptr(), mimo_o.data_ptr(), vptr(mimo_z, sdt), vptr(D, sdt), |
| angles.data_ptr(), dt.data_ptr<float>(), trap.data_ptr<float>(), |
| dA_cs.data_ptr<float>(), dA_cs_rev.data_ptr<float>(), vptr(norm_weight, sdt), |
| (float)norm_eps, fused, kv_ws.data_ptr(), out.data_ptr(), |
| B, S, H, Gqk, R, N, P, Na, C, c10::cuda::getCurrentCUDAStream().stream()); |
| C10_CUDA_KERNEL_LAUNCH_CHECK(); |
| return out; |
| } |
|
|
| #define BWD_ARGS \ |
| const void* q, const void* k, const void* v, const void* z, const void* q_bias, \ |
| const void* k_bias, const void* mimo_v, const void* mimo_o, const void* mimo_z, \ |
| const void* Dvec, const float* angles, const float* dt, const float* trap, \ |
| const float* dA_cs, const float* dA_cs_rev, const float* states, const void* norm_w, \ |
| float norm_eps, int fused_norm, const void* dy, float* dO_ws, float* dSt_ws, float* ws, \ |
| float* dq, float* dk, float* dv, float* dz, float* dq_bias, float* dk_bias, \ |
| float* dmimo_v, float* dmimo_o, float* dmimo_z, float* dD, float* dnorm_w, \ |
| float* d_dA_cs, float* d_dA_cs_rev, float* ddt, float* dtrap, float* dang, \ |
| int B, int S, int H, int Gqk, int R, int N, int P, int Na, int C, void* stream |
|
|
| void mamba3_mimo_bwd_launch_f32(BWD_ARGS); |
| void mamba3_mimo_bwd_launch_bf16(BWD_ARGS); |
| void mamba3_mimo_states_launch_f32( |
| const void* k, const void* v, const void* k_bias, const void* mimo_v, |
| const float* angles, const float* dt, const float* trap, |
| const float* dA_cs, const float* dA_cs_rev, float* kv_ws, |
| int B, int S, int H, int Gqk, int R, int N, int P, int Na, int C, void* stream); |
|
|
| std::vector<at::Tensor> mamba3_mimo_bwd( |
| torch::Tensor q, torch::Tensor k, torch::Tensor v, c10::optional<at::Tensor> z, |
| torch::Tensor q_bias, torch::Tensor k_bias, torch::Tensor mimo_v, torch::Tensor mimo_o, |
| c10::optional<at::Tensor> mimo_z, c10::optional<at::Tensor> D, |
| torch::Tensor angles, torch::Tensor dt, torch::Tensor trap, |
| torch::Tensor dA_cs, torch::Tensor dA_cs_rev, int64_t chunk_size, |
| c10::optional<at::Tensor> norm_weight, double norm_eps, torch::Tensor dy) |
| { |
| const auto sdt = q.scalar_type(); |
| TORCH_CHECK(sdt == at::kFloat || sdt == at::kBFloat16, |
| "backward inputs must be float32 or bfloat16"); |
| |
| |
| for (const auto& t : {q, k, v, q_bias, k_bias, mimo_v, mimo_o}) |
| TORCH_CHECK(t.is_cuda() && t.is_contiguous() && t.scalar_type() == sdt, |
| "all value inputs must be contiguous CUDA tensors of one dtype"); |
| M3_F32(dt); M3_F32(trap); M3_F32(dA_cs); M3_F32(dA_cs_rev); M3_F32(angles); |
|
|
| const int B = (int)q.size(0), S = (int)q.size(1), R = (int)q.size(2); |
| const int Gqk = (int)q.size(3), N = (int)q.size(4); |
| const int H = (int)v.size(2), P = (int)v.size(3), Na = (int)angles.size(3); |
| TORCH_CHECK(chunk_size > 0, "chunk_size must be positive"); |
| TORCH_CHECK(R <= 8, "mimo_rank above 8 is not supported"); |
| const int C = (int)chunk_size, Nc = (S + C - 1) / C, CR = C * R; |
|
|
| |
| |
| const size_t smem = std::max(mamba3_bwd_smem_bytes(R, N, P, C), |
| mamba3_states_smem_bytes(R, N, P, C)); |
| const size_t cap = (size_t)mamba3_max_smem_optin(); |
| TORCH_CHECK(smem <= cap, |
| "backward with chunk_size ", C, ", mimo_rank ", R, ", headdim ", P, |
| " and state ", N, " needs ", smem / 1024, " KB of shared memory per " |
| "block, above this device's ", cap / 1024, " KB opt-in limit; reduce " |
| "chunk_size * mimo_rank or headdim"); |
|
|
| auto f32 = v.options().dtype(at::kFloat); |
| |
| auto kf = k.to(at::kFloat).contiguous(), vf = v.to(at::kFloat).contiguous(); |
| auto kbf = k_bias.to(at::kFloat).contiguous(), mvf = mimo_v.to(at::kFloat).contiguous(); |
| auto states = torch::empty({B, H, Nc, N, P}, f32); |
| mamba3_mimo_states_launch_f32( |
| kf.data_ptr(), vf.data_ptr(), kbf.data_ptr(), mvf.data_ptr(), |
| angles.data_ptr<float>(), dt.data_ptr<float>(), trap.data_ptr<float>(), |
| dA_cs.data_ptr<float>(), dA_cs_rev.data_ptr<float>(), states.data_ptr<float>(), |
| B, S, H, Gqk, R, N, P, Na, C, c10::cuda::getCurrentCUDAStream().stream()); |
| C10_CUDA_KERNEL_LAUNCH_CHECK(); |
|
|
| auto dO_ws = torch::empty({B, H, Nc, CR, P}, f32); |
| auto dSt_ws = torch::empty({B, H, Nc, N, P}, f32); |
| |
| |
| const int64_t nblk = (int64_t)B * Nc; |
| const int64_t gy = ((int64_t)N * P + 255) / 256; |
| const int64_t nws = nblk * ((int64_t)3 * H * R * P + 2 * H * R * N + H * P + H) |
| + 2LL * B * H * S + (int64_t)B * H * Nc * gy; |
| auto ws = torch::empty({nws}, f32); |
|
|
| auto dq = torch::zeros_like(q, f32), dk = torch::zeros_like(k, f32); |
| auto dv = torch::zeros_like(v, f32), dz = torch::zeros_like(v, f32); |
| auto dqb = torch::zeros_like(q_bias, f32), dkb = torch::zeros_like(k_bias, f32); |
| auto dmv = torch::zeros_like(mimo_v, f32), dmo = torch::zeros_like(mimo_o, f32); |
| auto dmz = torch::zeros_like(mimo_v, f32); |
| auto dD = torch::zeros({H}, f32), dnw = torch::zeros({H, P}, f32); |
| auto dacs = torch::zeros_like(dA_cs), dacsr = torch::zeros_like(dA_cs_rev); |
| auto ddt = torch::zeros_like(dt), dtrap = torch::zeros_like(trap); |
| auto dang = torch::zeros_like(angles); |
|
|
| const int fused = (norm_weight.has_value() && norm_weight->defined()) ? 1 : 0; |
| auto bwd = (sdt == at::kFloat) ? mamba3_mimo_bwd_launch_f32 : mamba3_mimo_bwd_launch_bf16; |
| bwd(q.data_ptr(), k.data_ptr(), v.data_ptr(), vptr(z, sdt), q_bias.data_ptr(), |
| k_bias.data_ptr(), mimo_v.data_ptr(), mimo_o.data_ptr(), vptr(mimo_z, sdt), |
| vptr(D, sdt), angles.data_ptr<float>(), dt.data_ptr<float>(), |
| trap.data_ptr<float>(), dA_cs.data_ptr<float>(), dA_cs_rev.data_ptr<float>(), |
| states.data_ptr<float>(), vptr(norm_weight, sdt), (float)norm_eps, fused, |
| dy.contiguous().data_ptr(), dO_ws.data_ptr<float>(), dSt_ws.data_ptr<float>(), |
| ws.data_ptr<float>(), |
| dq.data_ptr<float>(), dk.data_ptr<float>(), dv.data_ptr<float>(), |
| dz.data_ptr<float>(), dqb.data_ptr<float>(), dkb.data_ptr<float>(), |
| dmv.data_ptr<float>(), dmo.data_ptr<float>(), dmz.data_ptr<float>(), |
| dD.data_ptr<float>(), dnw.data_ptr<float>(), dacs.data_ptr<float>(), |
| dacsr.data_ptr<float>(), ddt.data_ptr<float>(), dtrap.data_ptr<float>(), |
| dang.data_ptr<float>(), |
| B, S, H, Gqk, R, N, P, Na, C, c10::cuda::getCurrentCUDAStream().stream()); |
| C10_CUDA_KERNEL_LAUNCH_CHECK(); |
|
|
| return {dq, dk, dv, dz, dqb, dkb, dmv, dmo, dmz, dD, dnw, dacs, dacsr, ddt, dtrap, dang}; |
| } |
|
|
| |
| |
| #if defined(__has_include) && __has_include("registration.h") |
| # include "registration.h" |
| # define MAMBA3_HAVE_REGISTRATION 1 |
| #else |
| # define TORCH_LIBRARY_EXPAND(NAME, MOD) TORCH_LIBRARY(NAME, MOD) |
| # define CUDA_KERNEL 1 |
| #endif |
|
|
| TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, ops) { |
| |
| |
| ops.def("mamba3_fwd_dispatch(int mimo_rank, int dstate, int headdim, " |
| "int chunk_size, bool bf16) -> int[]", &mamba3_fwd_dispatch); |
| ops.def("mamba3_cumulative_angles(Tensor angles, Tensor dt) -> Tensor"); |
| ops.def("mamba3_mimo_step(Tensor q, Tensor k, Tensor v, Tensor? z, Tensor adt, " |
| "Tensor dt, Tensor trap, Tensor q_bias, Tensor k_bias, Tensor angles, " |
| "Tensor mimo_v, Tensor mimo_o, Tensor? mimo_z, Tensor? D, " |
| "Tensor angle_in, Tensor(a!) angle_out, Tensor(b!) S_st, " |
| "Tensor kprev_in, Tensor vprev_in, Tensor(c!) kprev_out, " |
| "Tensor(d!) vprev_out) -> Tensor"); |
| ops.def("mamba3_mimo_fwd(Tensor q, Tensor k, Tensor v, Tensor? z, Tensor q_bias, " |
| "Tensor k_bias, Tensor mimo_v, Tensor mimo_o, Tensor? mimo_z, Tensor? D, " |
| "Tensor angles, Tensor dt, Tensor trap, Tensor dA_cs, Tensor dA_cs_rev, " |
| "int chunk_size, Tensor? norm_weight, float norm_eps) -> Tensor"); |
|
|
| ops.def("mamba3_mimo_bwd(Tensor q, Tensor k, Tensor v, Tensor? z, Tensor q_bias, " |
| "Tensor k_bias, Tensor mimo_v, Tensor mimo_o, Tensor? mimo_z, Tensor? D, " |
| "Tensor angles, Tensor dt, Tensor trap, Tensor dA_cs, Tensor dA_cs_rev, " |
| "int chunk_size, Tensor? norm_weight, float norm_eps, Tensor dy) -> Tensor[]"); |
|
|
| #if defined(CUDA_KERNEL) || defined(ROCM_KERNEL) |
| ops.impl("mamba3_cumulative_angles", torch::kCUDA, &mamba3_cumulative_angles); |
| ops.impl("mamba3_mimo_step", torch::kCUDA, &mamba3_mimo_step); |
| ops.impl("mamba3_mimo_fwd", torch::kCUDA, &mamba3_mimo_fwd); |
| ops.impl("mamba3_mimo_bwd", torch::kCUDA, &mamba3_mimo_bwd); |
| #endif |
| } |
|
|
| #ifdef MAMBA3_HAVE_REGISTRATION |
| REGISTER_EXTENSION(TORCH_EXTENSION_NAME) |
| #endif |
|
|