| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <torch/extension.h> |
| #include <vector> |
|
|
| |
| namespace ttt_cuda { |
|
|
| |
| torch::Tensor silu_glu(const torch::Tensor& gate, const torch::Tensor& up); |
|
|
| |
| |
| |
| std::vector<torch::Tensor> silu_bwd_glu( |
| const torch::Tensor& dhidden, |
| const torch::Tensor& gate, |
| const torch::Tensor& up); |
|
|
| |
| |
| |
| torch::Tensor frob_norm_update( |
| const torch::Tensor& w, |
| const torch::Tensor& grad, |
| const torch::Tensor& w_init_norm, |
| int64_t norm_dim); |
|
|
| |
| std::vector<torch::Tensor> silu_derivs(const torch::Tensor& x); |
| torch::Tensor frobnorm_bwd(const torch::Tensor& gy, const torch::Tensor& x, double eps); |
| torch::Tensor weightnorm_bwd(const torch::Tensor& gy, const torch::Tensor& w_pre, |
| const torch::Tensor& wn_target, double eps); |
| torch::Tensor infer_step(const torch::Tensor& q, const torch::Tensor& w0, |
| const torch::Tensor& w2, const torch::Tensor& w1, |
| const torch::Tensor& o_norm_weight, double eps); |
| torch::Tensor infer_step_mid(const torch::Tensor& q, const torch::Tensor& w0, |
| const torch::Tensor& w2, const torch::Tensor& w1, |
| const torch::Tensor& o_norm_weight, double eps, double qeps); |
|
|
| } |
|
|
| |
| static inline torch::Tensor frob_normalize(const torch::Tensor& g) { |
| |
| auto nrm = g.flatten(1).norm(2, 1, true).unsqueeze(-1); |
| return g / (nrm + 1e-7); |
| } |
|
|
| |
| |
| static void fw_update( |
| torch::Tensor& w0, torch::Tensor& w1, torch::Tensor& w2, |
| const torch::Tensor& ki, const torch::Tensor& vi, |
| const torch::Tensor& lr0i, const torch::Tensor& lr1i, const torch::Tensor& lr2i, |
| const torch::Tensor& w0_norm, const torch::Tensor& w1_norm, const torch::Tensor& w2_norm) { |
|
|
| auto gate = ki.bmm(w0); |
| auto up = ki.bmm(w2); |
| auto hidden = ttt_cuda::silu_glu(gate, up); |
|
|
| auto dhidden = vi.bmm(w1.transpose(-1, -2)); |
| auto chain = ttt_cuda::silu_bwd_glu(dhidden, gate, up); |
| auto dgate_before_act = chain[0]; |
| auto dhidden_before_mul = chain[1]; |
|
|
| |
| auto w1_grad = frob_normalize( |
| (hidden * lr1i).to(vi.dtype()).transpose(-1, -2).bmm(vi)); |
| auto w0_grad = frob_normalize( |
| (ki * lr0i).to(dgate_before_act.dtype()).transpose(-1, -2).bmm(dgate_before_act)); |
| auto w2_grad = frob_normalize( |
| (ki * lr2i).to(dhidden_before_mul.dtype()).transpose(-1, -2).bmm(dhidden_before_mul)); |
|
|
| w1 = ttt_cuda::frob_norm_update(w1, w1_grad, w1_norm, 1); |
| w0 = ttt_cuda::frob_norm_update(w0, w0_grad, w0_norm, 1); |
| w2 = ttt_cuda::frob_norm_update(w2, w2_grad, w2_norm, 1); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| static std::array<torch::Tensor, 9> fw_update_save( |
| torch::Tensor& w0, torch::Tensor& w1, torch::Tensor& w2, |
| const torch::Tensor& ki, const torch::Tensor& vi, |
| const torch::Tensor& lr0i, const torch::Tensor& lr1i, const torch::Tensor& lr2i, |
| const torch::Tensor& w0_norm, const torch::Tensor& w1_norm, const torch::Tensor& w2_norm) { |
|
|
| auto gate = ki.bmm(w0); |
| auto up = ki.bmm(w2); |
| auto hidden = ttt_cuda::silu_glu(gate, up); |
| auto dhidden = vi.bmm(w1.transpose(-1, -2)); |
| auto chain = ttt_cuda::silu_bwd_glu(dhidden, gate, up); |
| auto dgate_before_act = chain[0]; |
| auto dhidden_before_mul = chain[1]; |
|
|
| auto fn1_in = (hidden * lr1i).to(vi.dtype()).transpose(-1, -2).bmm(vi); |
| auto fn0_in = (ki * lr0i).to(dgate_before_act.dtype()).transpose(-1, -2).bmm(dgate_before_act); |
| auto fn2_in = (ki * lr2i).to(dhidden_before_mul.dtype()).transpose(-1, -2).bmm(dhidden_before_mul); |
| auto w1_grad = frob_normalize(fn1_in); |
| auto w0_grad = frob_normalize(fn0_in); |
| auto w2_grad = frob_normalize(fn2_in); |
|
|
| auto w0_pre = w0 + w0_grad; |
| auto w1_pre = w1 + w1_grad; |
| auto w2_pre = w2 + w2_grad; |
|
|
| w1 = ttt_cuda::frob_norm_update(w1, w1_grad, w1_norm, 1); |
| w0 = ttt_cuda::frob_norm_update(w0, w0_grad, w0_norm, 1); |
| w2 = ttt_cuda::frob_norm_update(w2, w2_grad, w2_norm, 1); |
|
|
| return {gate, up, dhidden, fn0_in, fn1_in, fn2_in, w0_pre, w1_pre, w2_pre}; |
| } |
|
|
| |
| static torch::Tensor fw_apply( |
| const torch::Tensor& qi, const torch::Tensor& w0, |
| const torch::Tensor& w1, const torch::Tensor& w2) { |
| auto gate = qi.bmm(w0); |
| auto up = qi.bmm(w2); |
| auto h = ttt_cuda::silu_glu(gate, up); |
| return h.bmm(w1); |
| } |
|
|
| std::vector<torch::Tensor> causal_ttt_forward( |
| torch::Tensor w0, torch::Tensor w1, torch::Tensor w2, |
| torch::Tensor q, torch::Tensor k, torch::Tensor v, |
| torch::Tensor lr0, torch::Tensor lr1, torch::Tensor lr2, |
| int64_t chunk_size, |
| c10::optional<torch::Tensor> vlm_k, |
| c10::optional<torch::Tensor> vlm_v, |
| c10::optional<torch::Tensor> vlm_lr0, |
| c10::optional<torch::Tensor> vlm_lr1, |
| c10::optional<torch::Tensor> vlm_lr2) { |
|
|
| TORCH_CHECK(q.is_cuda(), "causal_ttt_forward: inputs must be CUDA tensors"); |
|
|
| |
| auto w0_norm = w0.detach().norm(2, 1, true); |
| auto w1_norm = w1.detach().norm(2, 1, true); |
| auto w2_norm = w2.detach().norm(2, 1, true); |
|
|
| |
| if (vlm_k.has_value()) { |
| fw_update(w0, w1, w2, |
| vlm_k.value(), vlm_v.value(), |
| vlm_lr0.value(), vlm_lr1.value(), vlm_lr2.value(), |
| w0_norm, w1_norm, w2_norm); |
| } |
|
|
| const int64_t L = q.size(1); |
| std::vector<torch::Tensor> outs; |
| for (int64_t s = 0; s < L; s += chunk_size) { |
| int64_t e = std::min(s + chunk_size, L); |
| using torch::indexing::Slice; |
|
|
| |
| auto qi = q.index({Slice(), Slice(s, e), Slice()}); |
| outs.push_back(fw_apply(qi, w0, w1, w2)); |
|
|
| |
| auto ki = k.index({Slice(), Slice(s, e), Slice()}); |
| auto vi = v.index({Slice(), Slice(s, e), Slice()}); |
| auto l0 = lr0.index({Slice(), Slice(s, e), Slice()}); |
| auto l1 = lr1.index({Slice(), Slice(s, e), Slice()}); |
| auto l2 = lr2.index({Slice(), Slice(s, e), Slice()}); |
| fw_update(w0, w1, w2, ki, vi, l0, l1, l2, w0_norm, w1_norm, w2_norm); |
| } |
|
|
| auto output = torch::cat(outs, 1); |
| return {output, w0, w1, w2}; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| std::vector<torch::Tensor> causal_ttt_forward_save( |
| torch::Tensor w0, torch::Tensor w1, torch::Tensor w2, |
| torch::Tensor q, torch::Tensor k, torch::Tensor v, |
| torch::Tensor lr0, torch::Tensor lr1, torch::Tensor lr2, |
| int64_t chunk_size, |
| c10::optional<torch::Tensor> vlm_k, |
| c10::optional<torch::Tensor> vlm_v, |
| c10::optional<torch::Tensor> vlm_lr0, |
| c10::optional<torch::Tensor> vlm_lr1, |
| c10::optional<torch::Tensor> vlm_lr2) { |
|
|
| TORCH_CHECK(q.is_cuda(), "causal_ttt_forward_save: inputs must be CUDA tensors"); |
| auto w0_norm = w0.detach().norm(2, 1, true); |
| auto w1_norm = w1.detach().norm(2, 1, true); |
| auto w2_norm = w2.detach().norm(2, 1, true); |
|
|
| auto pre_w0 = w0, pre_w1 = w1, pre_w2 = w2; |
| if (vlm_k.has_value()) { |
| fw_update(w0, w1, w2, vlm_k.value(), vlm_v.value(), |
| vlm_lr0.value(), vlm_lr1.value(), vlm_lr2.value(), |
| w0_norm, w1_norm, w2_norm); |
| } |
|
|
| const int64_t L = q.size(1); |
| using torch::indexing::Slice; |
| std::vector<torch::Tensor> outs, e0, e1, e2; |
| for (int64_t s = 0; s < L; s += chunk_size) { |
| int64_t e = std::min(s + chunk_size, L); |
| |
| e0.push_back(w0); e1.push_back(w1); e2.push_back(w2); |
| auto qi = q.index({Slice(), Slice(s, e), Slice()}); |
| outs.push_back(fw_apply(qi, w0, w1, w2)); |
| auto ki = k.index({Slice(), Slice(s, e), Slice()}); |
| auto vi = v.index({Slice(), Slice(s, e), Slice()}); |
| auto l0 = lr0.index({Slice(), Slice(s, e), Slice()}); |
| auto l1 = lr1.index({Slice(), Slice(s, e), Slice()}); |
| auto l2 = lr2.index({Slice(), Slice(s, e), Slice()}); |
| fw_update(w0, w1, w2, ki, vi, l0, l1, l2, w0_norm, w1_norm, w2_norm); |
| } |
| auto output = torch::cat(outs, 1); |
| auto entry_w0 = torch::stack(e0, 0); |
| auto entry_w1 = torch::stack(e1, 0); |
| auto entry_w2 = torch::stack(e2, 0); |
| return {output, w0, w1, w2, entry_w0, entry_w1, entry_w2, pre_w0, pre_w1, pre_w2}; |
| } |
|
|
| |
| |
| |
| |
| |
| |
|
|
| namespace { |
| using torch::indexing::Slice; |
| } |
|
|
| |
| static inline torch::Tensor silu_only(const torch::Tensor& x) { |
| return ttt_cuda::silu_glu(x, torch::ones_like(x)); |
| } |
|
|
| std::vector<torch::Tensor> causal_ttt_backward( |
| torch::Tensor w0, torch::Tensor w1, torch::Tensor w2, |
| torch::Tensor q, torch::Tensor k, torch::Tensor v, |
| torch::Tensor lr0, torch::Tensor lr1, torch::Tensor lr2, |
| int64_t chunk_size, |
| torch::Tensor g_out, torch::Tensor g_w0n, torch::Tensor g_w1n, torch::Tensor g_w2n, |
| c10::optional<torch::Tensor> vlm_k, |
| c10::optional<torch::Tensor> vlm_v, |
| c10::optional<torch::Tensor> vlm_lr0, |
| c10::optional<torch::Tensor> vlm_lr1, |
| c10::optional<torch::Tensor> vlm_lr2, |
| |
| |
| c10::optional<torch::Tensor> entry_w0 = c10::nullopt, |
| c10::optional<torch::Tensor> entry_w1 = c10::nullopt, |
| c10::optional<torch::Tensor> entry_w2 = c10::nullopt) { |
|
|
| TORCH_CHECK(q.is_cuda(), "causal_ttt_backward: inputs must be CUDA"); |
| const double FEPS = 1e-7, WEPS = 1e-5; |
| auto w0n_t = w0.norm(2, 1, true), w1n_t = w1.norm(2, 1, true), w2n_t = w2.norm(2, 1, true); |
| bool has_vlm = vlm_k.has_value(); |
| bool have_entry = entry_w0.has_value(); |
|
|
| const int64_t L = q.size(1); |
| std::vector<int64_t> starts; |
| for (int64_t s = 0; s < L; s += chunk_size) starts.push_back(s); |
|
|
| |
| std::vector<std::array<torch::Tensor, 3>> entry; |
| std::array<torch::Tensor, 3> pre_entry = {w0, w1, w2}; |
| if (have_entry) { |
| |
| auto ew0 = entry_w0.value(), ew1 = entry_w1.value(), ew2 = entry_w2.value(); |
| for (size_t ci = 0; ci < starts.size(); ++ci) { |
| entry.push_back({ew0.select(0, ci), ew1.select(0, ci), ew2.select(0, ci)}); |
| } |
| |
| } else { |
| |
| auto cw0 = w0, cw1 = w1, cw2 = w2; |
| if (has_vlm) { |
| fw_update(cw0, cw1, cw2, vlm_k.value(), vlm_v.value(), |
| vlm_lr0.value(), vlm_lr1.value(), vlm_lr2.value(), w0n_t, w1n_t, w2n_t); |
| } |
| for (size_t ci = 0; ci < starts.size(); ++ci) { |
| int64_t s = starts[ci], e = std::min(s + chunk_size, L); |
| entry.push_back({cw0, cw1, cw2}); |
| auto ki = k.index({Slice(), Slice(s, e), Slice()}); |
| auto vi = v.index({Slice(), Slice(s, e), Slice()}); |
| auto l0 = lr0.index({Slice(), Slice(s, e), Slice()}); |
| auto l1 = lr1.index({Slice(), Slice(s, e), Slice()}); |
| auto l2 = lr2.index({Slice(), Slice(s, e), Slice()}); |
| fw_update(cw0, cw1, cw2, ki, vi, l0, l1, l2, w0n_t, w1n_t, w2n_t); |
| } |
| } |
|
|
| |
| auto g_q = torch::zeros_like(q), g_k = torch::zeros_like(k), g_v = torch::zeros_like(v); |
| auto g_lr0 = torch::zeros_like(lr0), g_lr1 = torch::zeros_like(lr1), g_lr2 = torch::zeros_like(lr2); |
| auto gw0 = g_w0n.clone(), gw1 = g_w1n.clone(), gw2 = g_w2n.clone(); |
|
|
| |
| auto chunk_vjp = [&](const torch::Tensor& W0, const torch::Tensor& W1, const torch::Tensor& W2, |
| const torch::Tensor& qi, const torch::Tensor& ki, const torch::Tensor& vi, |
| const torch::Tensor& l0, const torch::Tensor& l1, const torch::Tensor& l2, |
| const torch::Tensor& g_oi, bool do_apply, |
| torch::Tensor& out_gw0, torch::Tensor& out_gw1, torch::Tensor& out_gw2, |
| torch::Tensor& out_gq, torch::Tensor& out_gk, torch::Tensor& out_gv, |
| torch::Tensor& out_gl0, torch::Tensor& out_gl1, torch::Tensor& out_gl2) { |
| |
| auto gate = ki.bmm(W0), up = ki.bmm(W2); |
| auto sd_g = ttt_cuda::silu_derivs(gate); |
| auto sg = silu_only(gate); |
| auto hidden = sg * up; |
| auto dhidden = vi.bmm(W1.transpose(-1, -2)); |
| auto dhid_bm = dhidden * sg; |
| auto dgate = dhidden * up; |
| auto mm = sd_g[0]; |
| auto dgba = dgate * mm; |
| |
| |
| auto A0 = (ki * l0).to(dgba.dtype()); |
| auto A1 = (hidden * l1).to(vi.dtype()); |
| auto A2 = (ki * l2).to(dhid_bm.dtype()); |
| auto fn1_in = A1.transpose(-1, -2).bmm(vi); |
| auto fn0_in = A0.transpose(-1, -2).bmm(dgba); |
| auto fn2_in = A2.transpose(-1, -2).bmm(dhid_bm); |
| auto rn1 = frob_normalize(fn1_in), rn0 = frob_normalize(fn0_in), rn2 = frob_normalize(fn2_in); |
| auto w0_pre = W0 + rn0, w1_pre = W1 + rn1, w2_pre = W2 + rn2; |
|
|
| |
| auto g_w0_pre = ttt_cuda::weightnorm_bwd(out_gw0, w0_pre, w0n_t, WEPS); |
| auto g_w1_pre = ttt_cuda::weightnorm_bwd(out_gw1, w1_pre, w1n_t, WEPS); |
| auto g_w2_pre = ttt_cuda::weightnorm_bwd(out_gw2, w2_pre, w2n_t, WEPS); |
| |
| auto g_w0_old = g_w0_pre.clone(), g_w1_old = g_w1_pre.clone(), g_w2_old = g_w2_pre.clone(); |
| auto g_rn0 = g_w0_pre, g_rn1 = g_w1_pre, g_rn2 = g_w2_pre; |
| |
| auto g_fn0 = ttt_cuda::frobnorm_bwd(g_rn0, fn0_in, FEPS); |
| auto g_fn1 = ttt_cuda::frobnorm_bwd(g_rn1, fn1_in, FEPS); |
| auto g_fn2 = ttt_cuda::frobnorm_bwd(g_rn2, fn2_in, FEPS); |
|
|
| |
| auto g_A1 = vi.bmm(g_fn1.transpose(-1, -2)); |
| auto g_vi = A1.bmm(g_fn1); |
| |
| auto g_hidden = (g_A1 * l1).to(up.dtype()); |
| out_gl1.index({Slice(), Slice(), Slice()}) += (g_A1 * hidden).sum(-1, true); |
| |
| auto g_A0 = dgba.bmm(g_fn0.transpose(-1, -2)); |
| auto g_dgba = A0.bmm(g_fn0); |
| auto g_ki = (g_A0 * l0).to(ki.dtype()); |
| out_gl0.index({Slice(), Slice(), Slice()}) += (g_A0 * ki).sum(-1, true); |
| |
| auto g_A2 = dhid_bm.bmm(g_fn2.transpose(-1, -2)); |
| auto g_dhid_bm = A2.bmm(g_fn2); |
| g_ki = g_ki + (g_A2 * l2).to(ki.dtype()); |
| out_gl2.index({Slice(), Slice(), Slice()}) += (g_A2 * ki).sum(-1, true); |
|
|
| |
| auto g_dgate = g_dgba * mm; |
| auto g_gate = g_dgba * dgate * sd_g[1]; |
| |
| auto g_dhidden = g_dgate * up; |
| auto g_up = g_dgate * dhidden; |
| |
| g_dhidden = g_dhidden + g_dhid_bm * sg; |
| auto g_sg = g_dhid_bm * dhidden; |
| |
| g_vi = g_vi + g_dhidden.bmm(W1); |
| g_w1_old = g_w1_old + g_dhidden.transpose(-1, -2).bmm(vi); |
| |
| g_sg = g_sg + g_hidden * up; |
| g_up = g_up + g_hidden * sg; |
| |
| g_ki = g_ki + g_up.bmm(W2.transpose(-1, -2)); |
| g_w2_old = g_w2_old + ki.transpose(-1, -2).bmm(g_up); |
| |
| g_gate = g_gate + g_sg * sd_g[0]; |
| |
| g_ki = g_ki + g_gate.bmm(W0.transpose(-1, -2)); |
| g_w0_old = g_w0_old + ki.transpose(-1, -2).bmm(g_gate); |
|
|
| |
| out_gk.index({Slice(), Slice(), Slice()}) += g_ki.to(out_gk.dtype()); |
| out_gv.index({Slice(), Slice(), Slice()}) += g_vi.to(out_gv.dtype()); |
|
|
| |
| if (do_apply) { |
| auto gate_q = qi.bmm(W0), up_q = qi.bmm(W2); |
| auto sq = silu_only(gate_q); |
| auto h_q = sq * up_q; |
| auto g_h_q = g_oi.bmm(W1.transpose(-1, -2)); |
| g_w1_old = g_w1_old + h_q.transpose(-1, -2).bmm(g_oi); |
| auto g_sq = g_h_q * up_q; |
| auto g_up_q = g_h_q * sq; |
| auto g_qi = g_up_q.bmm(W2.transpose(-1, -2)); |
| g_w2_old = g_w2_old + qi.transpose(-1, -2).bmm(g_up_q); |
| auto sd_q = ttt_cuda::silu_derivs(gate_q); |
| auto g_gate_q = g_sq * sd_q[0]; |
| g_qi = g_qi + g_gate_q.bmm(W0.transpose(-1, -2)); |
| g_w0_old = g_w0_old + qi.transpose(-1, -2).bmm(g_gate_q); |
| out_gq.index({Slice(), Slice(), Slice()}) += g_qi.to(out_gq.dtype()); |
| } |
|
|
| out_gw0 = g_w0_old; out_gw1 = g_w1_old; out_gw2 = g_w2_old; |
| }; |
|
|
| |
| for (int64_t idx = (int64_t)starts.size() - 1; idx >= 0; --idx) { |
| int64_t s = starts[idx], e = std::min(s + chunk_size, L); |
| auto W0 = entry[idx][0], W1 = entry[idx][1], W2 = entry[idx][2]; |
| auto qi = q.index({Slice(), Slice(s, e), Slice()}); |
| auto ki = k.index({Slice(), Slice(s, e), Slice()}); |
| auto vi = v.index({Slice(), Slice(s, e), Slice()}); |
| auto l0 = lr0.index({Slice(), Slice(s, e), Slice()}); |
| auto l1 = lr1.index({Slice(), Slice(s, e), Slice()}); |
| auto l2 = lr2.index({Slice(), Slice(s, e), Slice()}); |
| auto g_oi = g_out.index({Slice(), Slice(s, e), Slice()}); |
| auto gk_slice = g_k.index({Slice(), Slice(s, e), Slice()}); |
| auto gv_slice = g_v.index({Slice(), Slice(s, e), Slice()}); |
| auto gq_slice = g_q.index({Slice(), Slice(s, e), Slice()}); |
| auto gl0_slice = g_lr0.index({Slice(), Slice(s, e), Slice()}); |
| auto gl1_slice = g_lr1.index({Slice(), Slice(s, e), Slice()}); |
| auto gl2_slice = g_lr2.index({Slice(), Slice(s, e), Slice()}); |
| chunk_vjp(W0, W1, W2, qi, ki, vi, l0, l1, l2, g_oi, true, |
| gw0, gw1, gw2, gq_slice, gk_slice, gv_slice, gl0_slice, gl1_slice, gl2_slice); |
| } |
|
|
| std::vector<torch::Tensor> result = {gw0, gw1, gw2, g_q, g_k, g_v, g_lr0, g_lr1, g_lr2}; |
|
|
| |
| if (has_vlm) { |
| auto dummy_q = torch::Tensor(); |
| auto g_vk = torch::zeros_like(vlm_k.value()); |
| auto g_vv = torch::zeros_like(vlm_v.value()); |
| auto g_vl0 = torch::zeros_like(vlm_lr0.value()); |
| auto g_vl1 = torch::zeros_like(vlm_lr1.value()); |
| auto g_vl2 = torch::zeros_like(vlm_lr2.value()); |
| auto g_oi_dummy = torch::Tensor(); |
| chunk_vjp(pre_entry[0], pre_entry[1], pre_entry[2], dummy_q, |
| vlm_k.value(), vlm_v.value(), vlm_lr0.value(), vlm_lr1.value(), vlm_lr2.value(), |
| g_oi_dummy, false, |
| gw0, gw1, gw2, g_vk, g_vk, g_vv, g_vl0, g_vl1, g_vl2); |
| |
| result[0] = gw0; result[1] = gw1; result[2] = gw2; |
| result.push_back(g_vk); result.push_back(g_vv); |
| result.push_back(g_vl0); result.push_back(g_vl1); result.push_back(g_vl2); |
| } |
| return result; |
| } |
|
|
| PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { |
| m.def("causal_ttt_forward", &causal_ttt_forward, |
| "Causal block fast-weight SwiGLU TTT forward (CUDA)"); |
| m.def("causal_ttt_forward_save", &causal_ttt_forward_save, |
| "Forward that also saves per-chunk entry weights (Phase 1 no-recompute backward)"); |
| m.def("causal_ttt_backward", &causal_ttt_backward, |
| "Causal block fast-weight SwiGLU TTT backward (CUDA, Plan A)", |
| py::arg("w0"), py::arg("w1"), py::arg("w2"), |
| py::arg("q"), py::arg("k"), py::arg("v"), |
| py::arg("lr0"), py::arg("lr1"), py::arg("lr2"), |
| py::arg("chunk_size"), |
| py::arg("g_out"), py::arg("g_w0n"), py::arg("g_w1n"), py::arg("g_w2n"), |
| py::arg("vlm_k"), py::arg("vlm_v"), |
| py::arg("vlm_lr0"), py::arg("vlm_lr1"), py::arg("vlm_lr2"), |
| py::arg("entry_w0") = c10::nullopt, |
| py::arg("entry_w1") = c10::nullopt, |
| py::arg("entry_w2") = c10::nullopt); |
| |
| m.def("silu_derivs", &ttt_cuda::silu_derivs, "silu' and silu''"); |
| m.def("frobnorm_bwd", &ttt_cuda::frobnorm_bwd, "Frobenius-normalize vjp"); |
| m.def("weightnorm_bwd", &ttt_cuda::weightnorm_bwd, "weight-norm (per-col) vjp"); |
| m.def("infer_step", &ttt_cuda::infer_step, |
| "Fused single-token TTT inference apply + RMSNorm (CUDA)"); |
| m.def("infer_step_mid", &ttt_cuda::infer_step_mid, |
| "Stage-1 mega: fused q-norm + apply + RMSNorm (CUDA)"); |
| } |
|
|