1#![allow(non_snake_case)]
19
20use super::serial_recalculated::{k1_ssd_chunk_cumsum, k2_ssd_bmm, k4_ssd_state_passing};
21use crate::utils::fprim::{F, Mask, san};
22use burn::backend::Backend;
23use burn::tensor::s;
24
25#[non_exhaustive]
28pub struct CombinedGrads<B: Backend> {
29 pub d_x_bnlhp: F<B, 5>,
31 pub d_dt_discretized_bhnl: F<B, 4>,
33 pub d_b_bnlhr: F<B, 5>,
35 pub d_c_bnlhr: F<B, 5>,
37 pub d_d_h: F<B, 1>,
39 pub d_initial_state_bhpr: F<B, 4>,
41 pub d_a_decay_h: F<B, 1>,
43 #[cfg(test)]
48 pub d_da_local_bhnl: F<B, 4>,
49 #[cfg(test)]
53 pub d_dt_orange_bhnl: F<B, 4>,
54}
55
56pub fn k3_ssd_chunk_state_extended<B: Backend>(
68 x_bnlhp: F<B, 5>,
69 b_bnlhr: F<B, 5>,
70 da_cumsum_bhnl: F<B, 4>,
71 dt_discretized_bhnl: F<B, 4>,
72) -> (F<B, 5>, F<B, 4>, F<B, 4>, F<B, 5>) {
73 let [batch, nchunks, chunk_len, nheads, per_head_dim] = x_bnlhp.dims();
74 let [.., state_rank] = b_bnlhr.dims();
75
76 let x_bnhpl = x_bnlhp.permute([0, 1, 3, 4, 2]);
77 let b_bnhlr = b_bnlhr.permute([0, 1, 3, 2, 4]);
78
79 let da_cumsum_last_bhn1 = da_cumsum_bhnl.clone().slice(s![.., .., .., -1]);
81 let forward_decay_to_chunk_end_bhnl =
82 (da_cumsum_last_bhn1.expand([batch, nheads, nchunks, chunk_len]) - da_cumsum_bhnl).exp();
83 san(&forward_decay_to_chunk_end_bhnl);
84 let b_bar_scale_bhnl = forward_decay_to_chunk_end_bhnl.clone() * dt_discretized_bhnl;
85 san(&b_bar_scale_bhnl);
86
87 let b_bar_scale_bnhlr = b_bar_scale_bhnl
88 .clone()
89 .permute([0, 2, 1, 3]) .unsqueeze_dim::<5>(4) .expand([batch, nchunks, nheads, chunk_len, state_rank]); let b_scaled_bnhlr = b_bnhlr * b_bar_scale_bnhlr;
93 san(&b_scaled_bnhlr);
94
95 let intra_chunk_state_bnhpr = x_bnhpl.matmul(b_scaled_bnhlr.clone());
96 assert_eq!(
97 [batch, nchunks, nheads, per_head_dim, state_rank],
98 intra_chunk_state_bnhpr.dims()
99 );
100 san(&intra_chunk_state_bnhpr);
101
102 (
103 intra_chunk_state_bnhpr,
104 b_bar_scale_bhnl,
105 forward_decay_to_chunk_end_bhnl,
106 b_scaled_bnhlr,
107 )
108}
109
110#[allow(clippy::too_many_arguments)]
126pub fn combined_backward<B: Backend>(
127 d_y_bnlhp: F<B, 5>,
128 d_final_bhpr: F<B, 4>,
129 x_bnlhp: F<B, 5>,
131 dt_discretized_bhnl: F<B, 4>,
132 b_bnlhr: F<B, 5>,
133 c_bnlhr: F<B, 5>,
134 d_h: F<B, 1>,
135 initial_state_bhpr: F<B, 4>,
136 a_decay_h: F<B, 1>,
137) -> CombinedGrads<B> {
138 let [batch, nheads, nchunks, chunk_len] = dt_discretized_bhnl.dims();
139 let [.., per_head_dim] = x_bnlhp.dims();
140 let [.., state_rank] = b_bnlhr.dims();
141 let device = dt_discretized_bhnl.device();
142 let dtype = dt_discretized_bhnl.dtype();
143
144 san(&d_y_bnlhp);
145 san(&d_final_bhpr);
146 san(&x_bnlhp);
147 san(&dt_discretized_bhnl);
148 san(&b_bnlhr);
149 san(&c_bnlhr);
150 san(&d_h);
151 san(&initial_state_bhpr);
152 san(&a_decay_h);
153
154 let (da_cumsum_bhnl, da_chunk_end_bhn) =
160 k1_ssd_chunk_cumsum(dt_discretized_bhnl.clone(), a_decay_h.clone());
161 san(&da_cumsum_bhnl);
162
163 let cb_bnhll = k2_ssd_bmm(c_bnlhr.clone(), b_bnlhr.clone());
165 san(&cb_bnhll);
166
167 let (
169 intra_chunk_state_bnhpr,
170 b_bar_scale_bhnl,
171 forward_decay_to_chunk_end_bhnl,
172 b_scaled_bnhlr,
173 ) = k3_ssd_chunk_state_extended(
174 x_bnlhp.clone(),
175 b_bnlhr.clone(),
176 da_cumsum_bhnl.clone(),
177 dt_discretized_bhnl.clone(),
178 );
179
180 let (chunk_input_state_bnhpr, _final_state_bhpr) = k4_ssd_state_passing(
182 intra_chunk_state_bnhpr.clone(),
183 da_chunk_end_bhn.clone(),
184 initial_state_bhpr,
185 );
186 san(&chunk_input_state_bnhpr);
187
188 let d_d_h = (d_y_bnlhp.clone() * x_bnlhp.clone())
192 .permute([3, 0, 1, 2, 4]) .reshape([nheads, batch * nchunks * chunk_len * per_head_dim]) .sum_dim(1) .reshape([nheads]);
196 san(&d_d_h);
197
198 let d_x_skip_bnlhp = d_y_bnlhp.clone()
199 * d_h
200 .clone()
201 .unsqueeze_dims::<5>(&[0, 1, 2, 4]) .expand([batch, nchunks, chunk_len, nheads, per_head_dim]); san(&d_x_skip_bnlhp);
204
205 let causal_mask_ll: Mask<B> = Mask::tril_mask(chunk_len, chunk_len, 0, &device);
213
214 let mut vec_orange_d_x_bhlp: Vec<F<B, 4>> = Vec::with_capacity(nchunks);
215 let mut vec_orange_d_dt_bhl: Vec<F<B, 3>> = Vec::with_capacity(nchunks);
216 let mut vec_orange_d_da_bhl: Vec<F<B, 3>> = Vec::with_capacity(nchunks);
217 let mut vec_d_cb_bhll: Vec<F<B, 4>> = Vec::with_capacity(nchunks);
218 let mut vec_blue_d_c_bhlr: Vec<F<B, 4>> = Vec::with_capacity(nchunks);
219 let mut vec_blue_d_da_bhl: Vec<F<B, 3>> = Vec::with_capacity(nchunks);
220 let mut vec_d_intra_bhpr: Vec<F<B, 4>> = Vec::with_capacity(nchunks);
221 let mut vec_d_da_end_bh: Vec<F<B, 2>> = Vec::with_capacity(nchunks);
222
223 let mut d_running_state_bhpr: F<B, 4> = d_final_bhpr;
224
225 for i_chunk in (0..nchunks).rev() {
226 let da_cumsum_bhl: F<B, 3> = da_cumsum_bhnl
228 .clone()
229 .slice(s![.., .., i_chunk, ..]) .squeeze_dim::<3>(2); let dt_bhl: F<B, 3> = dt_discretized_bhnl
232 .clone()
233 .slice(s![.., .., i_chunk, ..]) .squeeze_dim::<3>(2); let x_bhlp: F<B, 4> = x_bnlhp
236 .clone()
237 .slice(s![.., i_chunk, .., .., ..]) .squeeze_dim::<4>(1) .permute([0, 2, 1, 3]); let d_y_bhlp: F<B, 4> = d_y_bnlhp
241 .clone()
242 .slice(s![.., i_chunk, .., .., ..]) .squeeze_dim::<4>(1) .permute([0, 2, 1, 3]); let c_bhlr: F<B, 4> = c_bnlhr
246 .clone()
247 .slice(s![.., i_chunk, .., .., ..]) .squeeze_dim::<4>(1) .permute([0, 2, 1, 3]); let cb_bhll: F<B, 4> = cb_bnhll
251 .clone()
252 .slice(s![.., i_chunk, .., .., ..]) .squeeze_dim::<4>(1); let chunk_input_state_bhpr: F<B, 4> = chunk_input_state_bnhpr
255 .clone()
256 .slice(s![.., i_chunk, .., .., ..]) .squeeze_dim::<4>(1); san(&chunk_input_state_bhpr);
259
260 let exp_da_cumsum_bhl: F<B, 3> = da_cumsum_bhl.clone().exp();
263 let exp_da_cumsum_bhlp: F<B, 4> = exp_da_cumsum_bhl
264 .clone()
265 .unsqueeze_dim::<4>(3) .expand([batch, nheads, chunk_len, per_head_dim]); let d_ch_bhlp: F<B, 4> = d_y_bhlp.clone() * exp_da_cumsum_bhlp.clone();
268 san(&d_ch_bhlp);
269
270 let d_chunk_input_state_bhpr: F<B, 4> = c_bhlr
272 .clone()
273 .permute([0, 1, 3, 2]) .matmul(d_ch_bhlp.clone()) .permute([0, 1, 3, 2]); san(&d_chunk_input_state_bhpr);
277
278 let d_c_blue_bhlr: F<B, 4> = d_ch_bhlp.clone().matmul(chunk_input_state_bhpr.clone());
280 san(&d_c_blue_bhlr);
281 vec_blue_d_c_bhlr.push(d_c_blue_bhlr);
282
283 let ch_bhlp: F<B, 4> = c_bhlr.clone().matmul(
285 chunk_input_state_bhpr.clone().permute([0, 1, 3, 2]), ); let d_da_blue_bhl: F<B, 3> = (d_y_bhlp.clone() * ch_bhlp * exp_da_cumsum_bhlp)
288 .sum_dim(3) .squeeze_dim::<3>(3); san(&d_da_blue_bhl);
291 vec_blue_d_da_bhl.push(d_da_blue_bhl);
292
293 let diff_bhll = {
297 let target_bhll = da_cumsum_bhl
298 .clone()
299 .unsqueeze_dim::<4>(3) .expand([batch, nheads, chunk_len, chunk_len]); let source_bhll = da_cumsum_bhl
302 .unsqueeze_dim::<4>(2) .expand([batch, nheads, chunk_len, chunk_len]); target_bhll - source_bhll
305 };
306 let causal_mask_bhll: Mask<B> = causal_mask_ll
307 .clone()
308 .reshape([1, 1, chunk_len, chunk_len]) .expand([batch, nheads, chunk_len, chunk_len]); let decay_bhll = diff_bhll
311 .mask_fill(causal_mask_bhll.clone(), f32::NEG_INFINITY)
312 .exp();
313 san(&decay_bhll);
314
315 let dt_source_bhll: F<B, 4> = dt_bhl
316 .unsqueeze_dim::<4>(2) .expand([batch, nheads, chunk_len, chunk_len]); let cb_decay_bhll = cb_bhll.clone() * decay_bhll.clone();
319 let w_bhll = cb_decay_bhll.clone() * dt_source_bhll.clone();
320
321 let d_orange_bhlp = d_y_bhlp; let d_w_bhll: F<B, 4> = d_orange_bhlp.clone().matmul(
324 x_bhlp.permute([0, 1, 3, 2]), );
326 san(&d_w_bhll);
327
328 let d_x_orange_bhlp: F<B, 4> = w_bhll
330 .permute([0, 1, 3, 2]) .matmul(d_orange_bhlp);
332 san(&d_x_orange_bhlp);
333 vec_orange_d_x_bhlp.push(d_x_orange_bhlp);
334
335 let d_w_masked_bhll = d_w_bhll.mask_fill(causal_mask_bhll, 0.);
337 let d_cb_decay_bhll = d_w_masked_bhll.clone() * dt_source_bhll;
338 san(&d_cb_decay_bhll);
339
340 let d_dt_orange_bhl: F<B, 3> = (d_w_masked_bhll * cb_decay_bhll)
342 .sum_dim(2) .squeeze_dim::<3>(2); san(&d_dt_orange_bhl);
345 vec_orange_d_dt_bhl.push(d_dt_orange_bhl);
346
347 let d_cb_bhll = d_cb_decay_bhll.clone() * decay_bhll.clone();
349 vec_d_cb_bhll.push(d_cb_bhll);
350 let d_decay_bhll = d_cb_decay_bhll * cb_bhll;
351 let d_diff_bhll = d_decay_bhll * decay_bhll;
352
353 let d_da_tgt_bhl: F<B, 3> = d_diff_bhll
355 .clone()
356 .sum_dim(3) .squeeze_dim::<3>(3); let d_da_src_bhl: F<B, 3> = d_diff_bhll
359 .sum_dim(2) .squeeze_dim::<3>(2); let d_da_orange_bhl = d_da_tgt_bhl - d_da_src_bhl;
362 san(&d_da_orange_bhl);
363 vec_orange_d_da_bhl.push(d_da_orange_bhl);
364
365 vec_d_intra_bhpr.push(d_running_state_bhpr.clone());
371
372 let decay_chunk_bhpr: F<B, 4> = da_chunk_end_bhn
373 .clone()
374 .slice(s![.., .., i_chunk]) .exp() .unsqueeze_dim::<4>(3) .expand([batch, nheads, per_head_dim, state_rank]); san(&decay_chunk_bhpr);
379
380 let d_decay_chunk_bhpr = d_running_state_bhpr.clone() * chunk_input_state_bhpr;
381 let d_da_chunk_end_bh: F<B, 2> = (d_decay_chunk_bhpr * decay_chunk_bhpr.clone())
383 .reshape([batch, nheads, per_head_dim * state_rank]) .sum_dim(2) .squeeze_dim::<2>(2); san(&d_da_chunk_end_bh);
387 vec_d_da_end_bh.push(d_da_chunk_end_bh);
388
389 d_running_state_bhpr = decay_chunk_bhpr * d_running_state_bhpr + d_chunk_input_state_bhpr;
390 san(&d_running_state_bhpr);
391 }
392 let d_initial_state_bhpr = d_running_state_bhpr;
394
395 vec_orange_d_x_bhlp.reverse();
397 vec_orange_d_dt_bhl.reverse();
398 vec_orange_d_da_bhl.reverse();
399 vec_d_cb_bhll.reverse();
400 vec_blue_d_c_bhlr.reverse();
401 vec_blue_d_da_bhl.reverse();
402 vec_d_intra_bhpr.reverse();
403 vec_d_da_end_bh.reverse();
404
405 let d_x_orange_bnlhp: F<B, 5> = F::stack::<5>(vec_orange_d_x_bhlp, 1).permute([0, 1, 3, 2, 4]);
407 let d_dt_orange_bhnl: F<B, 4> = F::stack(vec_orange_d_dt_bhl, 2);
408 let d_da_orange_bhnl: F<B, 4> = F::stack(vec_orange_d_da_bhl, 2);
409 let d_cb_bnhll: F<B, 5> = F::stack(vec_d_cb_bhll, 1);
410 let d_da_blue_bhnl: F<B, 4> = F::stack(vec_blue_d_da_bhl, 2);
411 let d_intra_chunk_state_bnhpr: F<B, 5> = F::stack(vec_d_intra_bhpr, 1);
412 let d_c_blue_bnhlr: F<B, 5> = F::stack(vec_blue_d_c_bhlr, 1);
413 let d_da_end_bhn: F<B, 3> = F::stack(vec_d_da_end_bh, 2);
414 san(&d_x_orange_bnlhp);
415 san(&d_dt_orange_bhnl);
416 san(&d_da_orange_bhnl);
417 san(&d_cb_bnhll);
418 san(&d_da_blue_bhnl);
419 san(&d_intra_chunk_state_bnhpr);
420 san(&d_c_blue_bnhlr);
421
422 let d_da_cumsum_k4_bhnl = {
425 let zeros = F::<B, 4>::zeros([batch, nheads, nchunks, chunk_len - 1], &device, dtype);
426 F::cat(vec![zeros, d_da_end_bhn.unsqueeze_dim::<4>(3)], 3)
427 };
428
429 let d_c_blue_bnlhr = d_c_blue_bnhlr.permute([0, 1, 3, 2, 4]);
430
431 let x_bnhpl = x_bnlhp.permute([0, 1, 3, 4, 2]);
443
444 let d_x_k3_bnlhp = d_intra_chunk_state_bnhpr
446 .clone()
447 .matmul(
448 b_scaled_bnhlr.clone().permute([0, 1, 2, 4, 3]), ) .permute([0, 1, 4, 2, 3]); san(&d_x_k3_bnlhp);
452
453 let d_b_scaled_bnhlr = x_bnhpl
455 .permute([0, 1, 2, 4, 3]) .matmul(d_intra_chunk_state_bnhpr);
457 san(&d_b_scaled_bnhlr);
458
459 let b_bar_scale_bnhlr = b_bar_scale_bhnl
461 .clone()
462 .permute([0, 2, 1, 3]) .unsqueeze_dim::<5>(4) .expand([batch, nchunks, nheads, chunk_len, state_rank]);
465 let d_b_k3_bnhlr = d_b_scaled_bnhlr.clone() * b_bar_scale_bnhlr;
466 let d_b_k3_bnlhr = d_b_k3_bnhlr.permute([0, 1, 3, 2, 4]);
467 san(&d_b_k3_bnlhr);
468
469 let b_bnhlr = b_bnlhr.clone().permute([0, 1, 3, 2, 4]);
470 let d_b_bar_scale_bhnl = (d_b_scaled_bnhlr * b_bnhlr)
471 .sum_dim(4) .squeeze_dim::<4>(4) .permute([0, 2, 1, 3]); san(&d_b_bar_scale_bhnl);
475
476 let d_forward_decay_bhnl = d_b_bar_scale_bhnl.clone() * dt_discretized_bhnl.clone();
480 let d_dt_discretized_k3_bhnl = d_b_bar_scale_bhnl * forward_decay_to_chunk_end_bhnl.clone();
481
482 let d_da_delta_bhnl = d_forward_decay_bhnl * forward_decay_to_chunk_end_bhnl;
484 let d_da_cumsum_sub_bhnl = -d_da_delta_bhnl.clone();
488 let d_da_cumsum_last_bhn = d_da_delta_bhnl
489 .sum_dim(3) .squeeze_dim::<3>(3); let d_da_cumsum_k3_bhnl = {
492 let zeros = F::<B, 4>::zeros([batch, nheads, nchunks, chunk_len - 1], &device, dtype);
493 d_da_cumsum_sub_bhnl + F::cat(vec![zeros, d_da_cumsum_last_bhn.unsqueeze_dim::<4>(3)], 3)
494 };
495 san(&d_da_cumsum_k3_bhnl);
496
497 let c_bnhlr = c_bnlhr.permute([0, 1, 3, 2, 4]);
504 let b_bnhlr = b_bnlhr.permute([0, 1, 3, 2, 4]);
505
506 let d_c_k2_bnhlr = d_cb_bnhll.clone().matmul(b_bnhlr.clone());
507 let d_c_k2_bnlhr = d_c_k2_bnhlr.permute([0, 1, 3, 2, 4]);
508 san(&d_c_k2_bnlhr);
509
510 let d_b_k2_bnhlr = d_cb_bnhll
511 .permute([0, 1, 2, 4, 3]) .matmul(c_bnhlr);
513 let d_b_k2_bnlhr = d_b_k2_bnhlr.permute([0, 1, 3, 2, 4]);
514 san(&d_b_k2_bnlhr);
515
516 #[cfg(test)]
524 let d_da_local_bhnl = d_da_blue_bhnl.clone() + d_da_orange_bhnl.clone();
525 #[cfg(test)]
526 san(&d_da_local_bhnl);
527
528 let d_da_cumsum_bhnl =
529 d_da_blue_bhnl + d_da_orange_bhnl + d_da_cumsum_k3_bhnl + d_da_cumsum_k4_bhnl;
530 san(&d_da_cumsum_bhnl);
531
532 let d_da_bhnl = {
536 let d_total_bhnl = d_da_cumsum_bhnl
537 .clone()
538 .sum_dim(3) .expand([batch, nheads, nchunks, chunk_len]);
540 let prefix_bhnl = d_da_cumsum_bhnl.cumsum(3);
541 let zeros_bhn1 = F::<B, 4>::zeros([batch, nheads, nchunks, 1], &device, dtype);
542 let prefix_shifted_bhnl =
543 F::cat(vec![zeros_bhn1, prefix_bhnl.narrow(3, 0, chunk_len - 1)], 3);
544 d_total_bhnl - prefix_shifted_bhnl
545 };
546 san(&d_da_bhnl);
547
548 let a_decay_111h1 = a_decay_h
550 .unsqueeze_dims::<4>(&[0, 2, 3])
551 .expand([batch, nheads, nchunks, chunk_len]);
552 let d_dt_k1_bhnl = d_da_bhnl.clone() * a_decay_111h1;
553 san(&d_dt_k1_bhnl);
554
555 let d_a_decay_h = (d_da_bhnl * dt_discretized_bhnl.clone())
557 .permute([1, 0, 2, 3]) .reshape([nheads, batch * nchunks * chunk_len]) .sum_dim(1) .squeeze_dim::<1>(1); san(&d_a_decay_h);
562
563 #[cfg(test)]
565 let d_dt_orange_bhnl_save = d_dt_orange_bhnl.clone();
566 let d_dt_discretized_bhnl = d_dt_orange_bhnl + d_dt_discretized_k3_bhnl + d_dt_k1_bhnl;
567 san(&d_dt_discretized_bhnl);
568
569 let d_x_bnlhp = d_x_skip_bnlhp + d_x_k3_bnlhp + d_x_orange_bnlhp;
570 san(&d_x_bnlhp);
571
572 let d_b_bnlhr = d_b_k2_bnlhr + d_b_k3_bnlhr;
573 san(&d_b_bnlhr);
574 let d_c_bnlhr = d_c_k2_bnlhr + d_c_blue_bnlhr;
575 san(&d_c_bnlhr);
576
577 CombinedGrads {
578 d_a_decay_h,
579 d_dt_discretized_bhnl,
580 d_x_bnlhp,
581 d_b_bnlhr,
582 d_c_bnlhr,
583 d_d_h,
584 d_initial_state_bhpr,
585 #[cfg(test)]
586 d_da_local_bhnl,
587 #[cfg(test)]
588 d_dt_orange_bhnl: d_dt_orange_bhnl_save,
589 }
590}
591
592#[cfg(all(test, feature = "_dev-test"))]
593mod tests;