Skip to main content

burn_mamba/mamba3/single_ssd/ssd/serial_recalculated/
diag.rs

1//! # Same-step γ-correction on primitives — forward and analytic backward
2//!
3//! Primitive (`F`) port of [`super::super::diag`](crate::mamba3::single_ssd::ssd::diag), plus the analytic backward
4//! the recompute node needs.  The forward is used by
5//! `super::serial_recalculated`'s K5; the backward by
6//! [`super::combined_backward`](crate::mamba3::single_ssd::ssd::serial_recalculated::combined_backward).  Both carry the same SISO fast path: at
7//! `mimo_rank == 1` the `m × m` Gram matrix is a scalar, so every matmul over
8//! the `mimo_rank` axis degenerates into a `1×K×1` / `1×1×N` GEMM and is
9//! replaced by a reduction plus broadcast multiplies.
10//!
11//! Forward (per `(b, n, l, h)`):
12//!
13//! ```text
14//!   qk_dot[m_out, m_in] = Σ_r C[m_out, r] · B[m_in, r]
15//!   y_diag[m_out, p]    = γ · Σ_{m_in} qk_dot[m_out, m_in] · V[m_in, p]
16//! ```
17
18#![allow(non_snake_case)]
19
20use burn_stack::utils::fprim::F;
21use burn::backend::Backend;
22
23/// Gradients produced by [`y_diag_correction_backward`] — the `y_diag` term's
24/// contribution to `d_v`, `d_c`, `d_b` and the whole of `d_gamma` (no other
25/// term consumes `γ`).
26pub struct DiagGrads<B: Backend> {
27    /// `y_diag`'s contribution to the gradient of `v`.
28    pub d_v_bnlmhp: F<B, 6>,
29    /// `y_diag`'s contribution to the gradient of `C`.
30    pub d_c_bnlmhr: F<B, 6>,
31    /// `y_diag`'s contribution to the gradient of `B`.
32    pub d_b_bnlmhr: F<B, 6>,
33    /// The gradient of `γ`.
34    pub d_gamma_bnlh: F<B, 4>,
35}
36
37/// The γ-weighted same-step correction `y_diag`, on primitives.
38///
39/// Dispatches on `mimo_rank` and `siso_specialization`; see
40/// [`super::super::diag::y_diag_correction`].
41pub fn y_diag_correction<B: Backend>(
42    v_bnlmhp: F<B, 6>,
43    b_bnlmhr: F<B, 6>,
44    c_bnlmhr: F<B, 6>,
45    gamma_bnlh: F<B, 4>,
46    siso_specialization: bool,
47) -> F<B, 6> {
48    let [.., mimo_rank, _nheads, _per_head_dim] = v_bnlmhp.dims();
49    if mimo_rank == 1 && siso_specialization {
50        y_diag_correction_siso(v_bnlmhp, b_bnlmhr, c_bnlmhr, gamma_bnlh)
51    } else {
52        y_diag_correction_mimo(v_bnlmhp, b_bnlmhr, c_bnlmhr, gamma_bnlh)
53    }
54}
55
56/// SISO (`mimo_rank == 1`) `y_diag` on primitives: a `state_rank` reduction plus
57/// a per-`(b, n, l, h)` scalar broadcast.
58pub(crate) fn y_diag_correction_siso<B: Backend>(
59    v_bnlmhp: F<B, 6>,
60    b_bnlmhr: F<B, 6>,
61    c_bnlmhr: F<B, 6>,
62    gamma_bnlh: F<B, 4>,
63) -> F<B, 6> {
64    let qk_dot_bnlmh1 = (c_bnlmhr * b_bnlmhr).sum_dim(5);
65    let gamma_bnl1h1 = gamma_bnlh.unsqueeze_dims::<6>(&[3, 5]);
66    v_bnlmhp * qk_dot_bnlmh1 * gamma_bnl1h1
67}
68
69/// General MIMO `y_diag` on primitives.
70pub(crate) fn y_diag_correction_mimo<B: Backend>(
71    v_bnlmhp: F<B, 6>,
72    b_bnlmhr: F<B, 6>,
73    c_bnlmhr: F<B, 6>,
74    gamma_bnlh: F<B, 4>,
75) -> F<B, 6> {
76    let c_bnlhmr = c_bnlmhr.swap_dims(3, 4);
77    let b_bnlhrm = b_bnlmhr.permute([0, 1, 2, 4, 5, 3]);
78    let qk_dot_bnlhmM = c_bnlhmr.matmul(b_bnlhrm); // bnlhm_outm_in
79    let v_bnlhmp = v_bnlmhp.swap_dims(3, 4);
80    let y_d_bnlhmp = qk_dot_bnlhmM.matmul(v_bnlhmp); // bnlhm_outp
81    let gamma_bnlh11 = gamma_bnlh.unsqueeze_dims::<6>(&[4, 5]);
82    (y_d_bnlhmp * gamma_bnlh11).swap_dims(3, 4)
83}
84
85/// Analytic backward of [`y_diag_correction`].
86///
87/// Has no recurrence, so it runs batched over all chunks at once. Dispatches on
88/// `mimo_rank` / `siso_specialization` exactly like the forward.
89pub fn y_diag_correction_backward<B: Backend>(
90    d_y_bnlmhp: F<B, 6>,
91    v_bnlmhp: F<B, 6>,
92    b_bnlmhr: F<B, 6>,
93    c_bnlmhr: F<B, 6>,
94    gamma_bnlh: F<B, 4>,
95    siso_specialization: bool,
96) -> DiagGrads<B> {
97    let [.., mimo_rank, _nheads, _per_head_dim] = v_bnlmhp.dims();
98    if mimo_rank == 1 && siso_specialization {
99        y_diag_correction_backward_siso(d_y_bnlmhp, v_bnlmhp, b_bnlmhr, c_bnlmhr, gamma_bnlh)
100    } else {
101        y_diag_correction_backward_mimo(d_y_bnlmhp, v_bnlmhp, b_bnlmhr, c_bnlmhr, gamma_bnlh)
102    }
103}
104
105/// SISO (`mimo_rank == 1`) backward.
106///
107/// With `qk = Σ_r C·B` and `dyv = Σ_p dY·V` both per-`(b, n, l, h)` scalars, the
108/// five MIMO matmuls become two reductions and four broadcast multiplies:
109///
110/// ```text
111///   d_gamma   = qk · dyv
112///   d_qk_dot  = γ · dyv
113///   d_v[p]    = (qk · γ) · dY[p]
114///   d_c[r]    = d_qk_dot · B[r]
115///   d_b[r]    = d_qk_dot · C[r]
116/// ```
117pub(crate) fn y_diag_correction_backward_siso<B: Backend>(
118    d_y_bnlmhp: F<B, 6>,
119    v_bnlmhp: F<B, 6>,
120    b_bnlmhr: F<B, 6>,
121    c_bnlmhr: F<B, 6>,
122    gamma_bnlh: F<B, 4>,
123) -> DiagGrads<B> {
124    let qk_dot_bnlmh1 = (c_bnlmhr.clone() * b_bnlmhr.clone()).sum_dim(5);
125    let dyv_bnlmh1 = (d_y_bnlmhp.clone() * v_bnlmhp).sum_dim(5);
126    let gamma_bnl1h1 = gamma_bnlh.unsqueeze_dims::<6>(&[3, 5]);
127
128    // d_gamma[b,n,l,h] = Σ_p dY · (qk_dot · V) = qk_dot · Σ_p dY·V
129    let d_gamma_bnlh: F<B, 4> = (qk_dot_bnlmh1.clone() * dyv_bnlmh1.clone())
130        .squeeze_dim::<5>(5) // bnlmh
131        .squeeze_dim::<4>(3); // bnlh  (mimo_rank == 1)
132
133    let d_qk_dot_bnlmh1 = dyv_bnlmh1 * gamma_bnl1h1.clone();
134    let d_v_bnlmhp = d_y_bnlmhp * (qk_dot_bnlmh1 * gamma_bnl1h1);
135    let d_c_bnlmhr = b_bnlmhr * d_qk_dot_bnlmh1.clone();
136    let d_b_bnlmhr = c_bnlmhr * d_qk_dot_bnlmh1;
137
138    DiagGrads {
139        d_v_bnlmhp,
140        d_c_bnlmhr,
141        d_b_bnlmhr,
142        d_gamma_bnlh,
143    }
144}
145
146/// General MIMO backward.
147pub(crate) fn y_diag_correction_backward_mimo<B: Backend>(
148    d_y_bnlmhp: F<B, 6>,
149    v_bnlmhp: F<B, 6>,
150    b_bnlmhr: F<B, 6>,
151    c_bnlmhr: F<B, 6>,
152    gamma_bnlh: F<B, 4>,
153) -> DiagGrads<B> {
154    let c_bnlhmr = c_bnlmhr.swap_dims(3, 4); // [b,n,l,h,m_out,r]
155    let b_bnlhmr = b_bnlmhr.swap_dims(3, 4); // [b,n,l,h,m_in,r]
156    let v_bnlhmp = v_bnlmhp.swap_dims(3, 4); // [b,n,l,h,m_in,p]
157    let d_y_bnlhmp = d_y_bnlmhp.swap_dims(3, 4); // [b,n,l,h,m_out,p]
158
159    // qk_dot[m_out, m_in] = Σ_r C[m_out,r] · B[m_in,r]
160    let qk_dot_bnlhmM = c_bnlhmr.clone().matmul(b_bnlhmr.clone().transpose());
161    // y_d_unweighted[m_out, p] = Σ_{m_in} qk_dot · V[m_in, p]
162    let y_d_unw_bnlhmp = qk_dot_bnlhmM.clone().matmul(v_bnlhmp.clone());
163
164    // d_gamma[b,n,l,h] = Σ_{m_out,p} d_y · y_d_unweighted
165    let d_gamma_bnlh: F<B, 4> = (d_y_bnlhmp.clone() * y_d_unw_bnlhmp)
166        .sum_dim(5) // bnlhm1
167        .squeeze_dim::<5>(5) // bnlhm
168        .sum_dim(4) // bnlh1
169        .squeeze_dim::<4>(4); // bnlh
170
171    // d_y_d_unweighted = γ · d_y  (γ broadcast over m_out, p)
172    let gamma_bnlh11 = gamma_bnlh.unsqueeze_dims::<6>(&[4, 5]);
173    let d_y_d_unw_bnlhmp = d_y_bnlhmp * gamma_bnlh11;
174
175    // d_qk_dot[m_out, m_in] = Σ_p d_y_d_unweighted[m_out, p] · V[m_in, p]
176    let d_qk_dot_bnlhmM = d_y_d_unw_bnlhmp.clone().matmul(v_bnlhmp.transpose()); // [b,n,l,h,m_out,m_in]
177
178    // d_v[m_in, p] = Σ_{m_out} qk_dot[m_out, m_in] · d_y_d_unweighted[m_out, p]
179    let d_v_bnlhmp = qk_dot_bnlhmM
180        .transpose() // qk_dotᵀ: [b,n,l,h,m_in,m_out]
181        .matmul(d_y_d_unw_bnlhmp); // [b,n,l,h,m_in,p]
182
183    // d_C[m_out, r] = Σ_{m_in} d_qk_dot[m_out, m_in] · B[m_in, r]
184    let d_c_bnlhmr = d_qk_dot_bnlhmM.clone().matmul(b_bnlhmr); // [b,n,l,h,m_out,r]
185    // d_B[m_in, r] = Σ_{m_out} d_qk_dot[m_out, m_in] · C[m_out, r]
186    let d_b_bnlhmr = d_qk_dot_bnlhmM
187        .transpose() // d_qk_dotᵀ: [b,n,l,h,m_in,m_out]
188        .matmul(c_bnlhmr); // [b,n,l,h,m_in,r]
189
190    DiagGrads {
191        d_v_bnlmhp: d_v_bnlhmp.swap_dims(3, 4),
192        d_c_bnlmhr: d_c_bnlhmr.swap_dims(3, 4),
193        d_b_bnlmhr: d_b_bnlhmr.swap_dims(3, 4),
194        d_gamma_bnlh,
195    }
196}
197
198// ---------------------------------------------------------------------------
199// Tests — SISO fast path ≡ MIMO-general path (forward and backward)
200// ---------------------------------------------------------------------------
201
202#[cfg(all(test, feature = "_dev-test"))]
203mod tests;