1#![allow(non_snake_case)]
19
20use crate::utils::fprim::{F, Mask, san};
21use burn::backend::tensor::{Device, FloatTensor};
22use burn::backend::*;
23use burn::backend::{Backend, backend_extension};
24
25pub(crate) enum RotPrim<B: Backend> {
32 Complex {
34 cum_bsha: F<B, 4>,
35 rope_dim: usize,
36 rotate_pairwise: bool,
37 },
38 Quaternion { cum_bshj4: F<B, 5> },
40}
41
42impl<B: Backend> RotPrim<B> {
43 pub fn wrap(
45 rot: FloatTensor<B>,
46 quaternion: bool,
47 rope_dim: usize,
48 rotate_pairwise: bool,
49 ) -> Self {
50 if quaternion {
51 RotPrim::Quaternion {
52 cum_bshj4: F::<B, 5>::new(rot),
53 }
54 } else {
55 RotPrim::Complex {
56 cum_bsha: F::<B, 4>::new(rot),
57 rope_dim,
58 rotate_pairwise,
59 }
60 }
61 }
62}
63
64pub(crate) fn chunk_decay<B: Backend>(da_blh: F<B, 3>) -> (F<B, 3>, F<B, 4>) {
72 let [batch, chunk_len, nheads] = da_blh.dims();
73 let device = da_blh.device();
74 let a_bhl = da_blh.permute([0, 2, 1]).cumsum(2);
75 let d_bhl = a_bhl.clone().exp();
76 let target_bhll = a_bhl
77 .clone()
78 .unsqueeze_dim::<4>(3)
79 .expand([batch, nheads, chunk_len, chunk_len]);
80 let source_bhll = a_bhl
81 .unsqueeze_dim::<4>(2)
82 .expand([batch, nheads, chunk_len, chunk_len]);
83 let above_diag = Mask::tril_mask(chunk_len, chunk_len, 0, &device)
84 .reshape([1, 1, chunk_len, chunk_len])
85 .expand([batch, nheads, chunk_len, chunk_len]);
86 let l_bhll = (target_bhll - source_bhll)
87 .mask_fill(above_diag, f32::NEG_INFINITY)
88 .exp();
89 (d_bhl, l_bhll)
90}
91
92pub(crate) fn chunk_states<B: Backend>(
100 xhat_blchp: F<B, 5>,
101 bhat_blchr: F<B, 5>,
102 d_bhl: &F<B, 3>,
103 l_bhll: &F<B, 4>,
104 h_in_bhpr: &F<B, 4>,
105) -> F<B, 5> {
106 let [batch, chunk_len, chan, nheads, per_head_dim] = xhat_blchp.dims();
107 let [.., state_rank] = bhat_blchr.dims();
108
109 let xhat_bh1jcp = xhat_blchp
110 .permute([0, 3, 1, 2, 4]) .unsqueeze_dim::<6>(2); let l_bhtj11 = l_bhll.clone().unsqueeze_dims::<6>(&[4, 5]);
113 let xw_bhtJp = (l_bhtj11
114 * xhat_bh1jcp.expand([batch, nheads, chunk_len, chunk_len, chan, per_head_dim]))
115 .reshape([batch, nheads, chunk_len, chunk_len * chan, per_head_dim]);
116
117 let bhat_bh1Jr = bhat_blchr
118 .permute([0, 3, 1, 2, 4]) .reshape([batch, nheads, 1, chunk_len * chan, state_rank])
120 .expand([batch, nheads, chunk_len, chunk_len * chan, state_rank]);
121
122 let intra_bhtpr = xw_bhtJp.permute([0, 1, 2, 4, 3]).matmul(bhat_bh1Jr);
123
124 let carried_bhtpr = d_bhl
125 .clone()
126 .unsqueeze_dims::<5>(&[3, 4])
127 .expand([batch, nheads, chunk_len, per_head_dim, state_rank])
128 * h_in_bhpr
129 .clone()
130 .unsqueeze_dim::<5>(2)
131 .expand([batch, nheads, chunk_len, per_head_dim, state_rank]);
132 carried_bhtpr + intra_bhtpr
133}
134
135pub(crate) fn chunk_writes<B: Backend>(xhat_blchp: F<B, 5>, bhat_blchr: F<B, 5>) -> F<B, 5> {
138 let xh_bhjpc = xhat_blchp.permute([0, 3, 1, 4, 2]); let bh_bhjcr = bhat_blchr.permute([0, 3, 1, 2, 4]); xh_bhjpc.matmul(bh_bhjcr)
141}
142
143pub(crate) fn quat_mul_prim<B: Backend, const D: usize>(a: F<B, D>, b: F<B, D>) -> F<B, D> {
146 let n = D - 1;
147 let comp = |t: &F<B, D>, i: usize| t.clone().narrow(n, i, 1);
148 let (aw, ax, ay, az) = (comp(&a, 0), comp(&a, 1), comp(&a, 2), comp(&a, 3));
149 let (bw, bx, by, bz) = (comp(&b, 0), comp(&b, 1), comp(&b, 2), comp(&b, 3));
150 let w = aw.clone() * bw.clone()
151 - ax.clone() * bx.clone()
152 - ay.clone() * by.clone()
153 - az.clone() * bz.clone();
154 let x = aw.clone() * bx.clone() + ax.clone() * bw.clone() + ay.clone() * bz.clone()
155 - az.clone() * by.clone();
156 let y = aw.clone() * by.clone() - ax.clone() * bz.clone()
157 + ay.clone() * bw.clone()
158 + az.clone() * bx.clone();
159 let z = aw * bz + ax * by - ay * bx + az * bw;
160 F::cat(vec![w, x, y, z], n)
161}
162
163pub(crate) fn quat_conj_prim<B: Backend, const D: usize>(q: F<B, D>) -> F<B, D> {
165 let n = D - 1;
166 let w = q.clone().narrow(n, 0, 1);
167 let xyz = q.narrow(n, 1, 3);
168 F::cat(vec![w, -xyz], n)
169}
170
171fn chunk_cos_sin<B: Backend>(
176 cum_bsha: &F<B, 4>,
177 start: usize,
178 len: usize,
179 per_head_dim: usize,
180 transpose: bool,
181) -> (F<B, 5>, F<B, 5>) {
182 let [batch, _seq, nheads, num_angles] = cum_bsha.dims();
183 let theta_bhlpa = cum_bsha
184 .clone()
185 .narrow(1, start, len)
186 .permute([0, 2, 1, 3]) .unsqueeze_dim::<5>(3)
188 .expand([batch, nheads, len, per_head_dim, num_angles]);
189 let theta = if transpose { theta_bhlpa } else { -theta_bhlpa };
190 (theta.clone().cos(), theta.sin())
191}
192
193pub(crate) fn rotate_chunk<B: Backend>(
202 v_bhlpr: F<B, 5>,
203 rot: &RotPrim<B>,
204 start: usize,
205 transpose: bool,
206) -> F<B, 5> {
207 let [batch, nheads, len, per_head_dim, state_rank] = v_bhlpr.dims();
208 match rot {
209 RotPrim::Complex {
210 cum_bsha,
211 rope_dim,
212 rotate_pairwise,
213 } => {
214 let rope_dim = *rope_dim;
215 if rope_dim == 0 {
216 return v_bhlpr;
217 }
218 let (cos, sin) = chunk_cos_sin::<B>(cum_bsha, start, len, per_head_dim, transpose);
219 if *rotate_pairwise {
221 let num_angles = rope_dim / 2;
223 let active = v_bhlpr.clone().narrow(4, 0, rope_dim).reshape([
224 batch,
225 nheads,
226 len,
227 per_head_dim,
228 num_angles,
229 2,
230 ]);
231 let x0 = active.clone().narrow(5, 0, 1).squeeze_dim::<5>(5);
232 let x1 = active.narrow(5, 1, 1).squeeze_dim::<5>(5);
233 let x0r = cos.clone() * x0.clone() - sin.clone() * x1.clone();
234 let x1r = sin * x0 + cos * x1;
235 let rotated = F::stack::<6>(vec![x0r, x1r], 5).reshape([
236 batch,
237 nheads,
238 len,
239 per_head_dim,
240 rope_dim,
241 ]);
242 if rope_dim == state_rank {
243 rotated
244 } else {
245 let tail = v_bhlpr.narrow(4, rope_dim, state_rank - rope_dim);
246 F::cat(vec![rotated, tail], 4)
247 }
248 } else {
249 let half = state_rank / 2;
251 let num_angles = rope_dim / 2;
252 let h1 = v_bhlpr.clone().narrow(4, 0, num_angles);
253 let h2 = v_bhlpr.clone().narrow(4, half, num_angles);
254 let h1r = cos.clone() * h1.clone() - sin.clone() * h2.clone();
255 let h2r = sin * h1 + cos * h2;
256 if num_angles == half {
257 F::cat(vec![h1r, h2r], 4)
258 } else {
259 let h1_pass = v_bhlpr.clone().narrow(4, num_angles, half - num_angles);
260 let h2_pass = v_bhlpr.narrow(4, half + num_angles, half - num_angles);
261 F::cat(vec![h1r, h1_pass, h2r, h2_pass], 4)
262 }
263 }
264 }
265 RotPrim::Quaternion { cum_bshj4 } => {
266 let blocks = cum_bshj4.dims()[3];
267 let rope_width = 4 * blocks;
268 let q_bhlpj4 = cum_bshj4
269 .clone()
270 .narrow(1, start, len)
271 .permute([0, 2, 1, 3, 4]) .unsqueeze_dim::<6>(3)
273 .expand([batch, nheads, len, per_head_dim, blocks, 4]);
274 let q = if transpose {
277 quat_conj_prim(q_bhlpj4)
278 } else {
279 q_bhlpj4
280 };
281 let active = v_bhlpr.clone().narrow(4, 0, rope_width).reshape([
282 batch,
283 nheads,
284 len,
285 per_head_dim,
286 blocks,
287 4,
288 ]);
289 let rotated = quat_mul_prim(q, active).reshape([
290 batch,
291 nheads,
292 len,
293 per_head_dim,
294 rope_width,
295 ]);
296 if rope_width == state_rank {
297 rotated
298 } else {
299 let tail = v_bhlpr.narrow(4, rope_width, state_rank - rope_width);
300 F::cat(vec![rotated, tail], 4)
301 }
302 }
303 }
304}
305
306pub(crate) fn chunk_mask<B: Backend>(
309 chunk_len: usize,
310 start: usize,
311 valid_len: usize,
312 device: &Device<B>,
313 dtype: FloatDType,
314) -> F<B, 5> {
315 let valid = valid_len.saturating_sub(start).min(chunk_len);
316 let ones = F::<B, 1>::full([valid], 1.0, device, dtype);
317 let mask = if valid < chunk_len {
318 F::cat(
319 vec![ones, F::<B, 1>::zeros([chunk_len - valid], device, dtype)],
320 0,
321 )
322 } else {
323 ones
324 };
325 mask.reshape([1, 1, chunk_len, 1, 1])
326}
327
328pub(crate) fn moments_phys_fwd<B: Backend>(
333 xhat_bnlchp: F<B, 6>,
334 bhat_bnlchr: F<B, 6>,
335 da_bnlh: F<B, 4>,
336 rot: &RotPrim<B>,
337 initial_state_bhpr: F<B, 4>,
338 valid_len: usize,
339) -> (F<B, 4>, F<B, 3>) {
340 let [batch, nchunks, chunk_len, _chan, nheads, per_head_dim] = xhat_bnlchp.dims();
341 let [.., state_rank] = bhat_bnlchr.dims();
342 let device = xhat_bnlchp.device();
343 let dtype = xhat_bnlchp.dtype();
344
345 let mut h_bhpr = initial_state_bhpr;
346 let mut m2_bhrr = F::<B, 4>::zeros([batch, nheads, state_rank, state_rank], &device, dtype);
347 let mut m1_bhr = F::<B, 3>::zeros([batch, nheads, state_rank], &device, dtype);
348
349 for n in 0..nchunks {
350 let start = n * chunk_len;
351 if start >= valid_len {
352 break;
353 }
354 let da_blh = da_bnlh.clone().narrow(1, n, 1).squeeze_dim::<3>(1);
355 let (d_bhl, l_bhll) = chunk_decay::<B>(da_blh);
356 let xhat_blchp = xhat_bnlchp.clone().narrow(1, n, 1).squeeze_dim::<5>(1);
357 let bhat_blchr = bhat_bnlchr.clone().narrow(1, n, 1).squeeze_dim::<5>(1);
358
359 let states_bhlpr = chunk_states::<B>(xhat_blchp, bhat_blchr, &d_bhl, &l_bhll, &h_bhpr);
360 h_bhpr = states_bhlpr
361 .clone()
362 .narrow(2, chunk_len - 1, 1)
363 .squeeze_dim::<4>(2);
364
365 let phys_bhlpr = rotate_chunk::<B>(states_bhlpr, rot, start, false);
366 let masked_bhlpr =
367 phys_bhlpr * chunk_mask::<B>(chunk_len, start, valid_len, &device, dtype).expand([
368 batch,
369 nheads,
370 chunk_len,
371 per_head_dim,
372 state_rank,
373 ]);
374
375 let masked_bhLr =
376 masked_bhlpr.reshape([batch, nheads, chunk_len * per_head_dim, state_rank]);
377 m2_bhrr = m2_bhrr
378 + masked_bhLr
379 .clone()
380 .permute([0, 1, 3, 2])
381 .matmul(masked_bhLr.clone());
382 m1_bhr = m1_bhr + masked_bhLr.sum_dim(2).squeeze_dim::<3>(2);
383 }
384 san(&m2_bhrr);
385 san(&m1_bhr);
386 (m2_bhrr, m1_bhr)
387}
388
389#[backend_extension(
399 Cpu: cfg(feature = "backend-cpu"),
400 Cuda: cfg(feature = "backend-cuda"),
401 Rocm: cfg(feature = "backend-rocm"),
402 Metal: cfg(feature = "backend-metal"),
403 Vulkan: cfg(feature = "backend-vulkan"),
404 Wgpu: cfg(feature = "backend-wgpu"),
405 WebGpu: cfg(feature = "backend-webgpu"),
406 Flex: cfg(feature = "backend-flex"),
407 NdArray: cfg(feature = "backend-ndarray"),
408 LibTorch: cfg(any(feature = "backend-tch-cpu", feature = "backend-tch-gpu")),
409 Autodiff: cfg(feature = "autodiff"),
410)]
411pub trait Mamba3MomentsBackendExt: Backend {
412 #[allow(clippy::too_many_arguments)]
421 fn mamba3_state_moments_phys(
422 xhat_bnlchp: FloatTensor<Self>,
423 bhat_bnlchr: FloatTensor<Self>,
424 da_bnlh: FloatTensor<Self>,
425 rot: FloatTensor<Self>,
426 initial_state_bhpr: FloatTensor<Self>,
427 valid_len: usize,
428 quaternion: bool,
429 rope_dim: usize,
430 rotate_pairwise: bool,
431 ) -> (FloatTensor<Self>, FloatTensor<Self>) {
432 let rot = RotPrim::<Self>::wrap(rot, quaternion, rope_dim, rotate_pairwise);
433 let (m2, m1) = moments_phys_fwd::<Self>(
434 F::<Self, 6>::new(xhat_bnlchp),
435 F::<Self, 6>::new(bhat_bnlchr),
436 F::<Self, 4>::new(da_bnlh),
437 &rot,
438 F::<Self, 4>::new(initial_state_bhpr),
439 valid_len,
440 );
441 (m2.inner(), m1.inner())
442 }
443}
444
445crate::impl_ssd_backend_ext_for_burn_backends!(Mamba3MomentsBackendExt);