1use crate::mamba3::double_ssd::double_ssd::StepProjection;
53use crate::mamba3::double_ssd::prelude::*;
54use crate::mamba3::helpers;
55use crate::mamba3::prelude::*;
56use crate::mamba3::rotation::{
57 RotationState, quat_conj, quat_from_scaled_axis, quat_mul, quat_normalize,
58 rotate_state_rank_blocks,
59};
60use crate::modules::sanity as san;
61use crate::modules::{apply_rope_partial, wrap_angle};
62use crate::utils::div_eps;
63use burn::prelude::*;
64use core::f32::consts::PI;
65
66impl Mamba3 {
71 pub fn step_infinite(&self, input_bd: Tensor<2>) -> Tensor<2> {
89 let StepProjection {
90 z_bi,
91 x_bhp,
92 b_bmhr,
93 c_bmhr,
94 rot_ba,
95 dt_bh,
96 da_bh: _,
97 alpha_bh,
98 beta_bh,
99 gamma_bh,
100 } = self.step_project(input_bd);
101 let [batch, mimo_rank, nheads, _state_rank] = b_bmhr.dims();
102 let eps = div_eps(alpha_bh.dtype());
103
104 let tail_bh = (beta_bh.clone() + gamma_bh.clone())
106 / (-alpha_bh.clone() + 1.0).clamp_min(eps);
107
108 let b_eff_bmhr = match self.rotation_kind() {
111 RotationKind::Complex2D => {
112 let theta_bha = per_step_angle(rot_ba, dt_bh); let (cos, sin) = cos_sin(theta_bha);
114 let a_bh1 = alpha_bh.unsqueeze_dim::<3>(2);
115 let beta_bh1 = beta_bh.unsqueeze_dim::<3>(2);
116 let gamma_bh1 = gamma_bh.unsqueeze_dim::<3>(2);
117 let num_re = gamma_bh1 + beta_bh1.clone() * cos.clone();
119 let num_im = -beta_bh1 * sin.clone();
120 let den_re = -a_bh1.clone() * cos + 1.0;
121 let den_im = a_bh1 * sin;
122 let (m_re, m_im) = complex_div(num_re, num_im, den_re, den_im);
123 mul_complex_partial(
124 b_bmhr,
125 m_re,
126 m_im,
127 tail_bh,
128 self.rope_dim,
129 mimo_rank == 1,
130 )
131 }
132 RotationKind::Quaternion4D => {
133 let g_bhj3 = per_step_generator(rot_ba, dt_bh, self.num_quat_blocks);
134 let q_bhj4 = quat_from_scaled_axis::<4>(g_bhj3); let alpha_bh11 = alpha_bh.unsqueeze_dims::<4>(&[2, 3]);
136 let beta_bh11 = beta_bh.unsqueeze_dims::<4>(&[2, 3]);
137 let gamma_bh11 = gamma_bh.unsqueeze_dims::<4>(&[2, 3]);
138 let num = quat_scalar_affine(gamma_bh11, beta_bh11, q_bhj4.clone());
140 let den_inv = quat_inv(quat_one_minus(q_bhj4 * alpha_bh11));
141 let f_bhj4 = quat_mul(num, den_inv);
142 mul_quat_partial(b_bmhr, f_bhj4, tail_bh, self.num_quat_blocks * 4)
143 }
144 };
145 san(&b_eff_bmhr);
146
147 let mimo_x_hmp = self.mimo_x_hmp.as_ref().map(|p| p.val());
149 let x_vals_bmhp = helpers::build_v_with_mimo::<3, 4>(x_bhp, mimo_x_hmp.as_ref(), 1);
150 let gram_bhmm = {
151 let c_bhmr = c_bmhr.permute([0, 2, 1, 3]);
152 let b_bhrm = b_eff_bmhr.permute([0, 2, 3, 1]);
153 c_bhmr.matmul(b_bhrm) };
155 let out_m_bmhp = {
156 let x_bhmp = x_vals_bmhp.clone().permute([0, 2, 1, 3]);
157 gram_bhmm.matmul(x_bhmp).permute([0, 2, 1, 3])
158 };
159 assert_eq!(
160 [batch, mimo_rank, nheads, self.per_head_dim()],
161 out_m_bmhp.dims()
162 );
163 san(&out_m_bmhp);
164
165 self.step_finish(out_m_bmhp, x_vals_bmhp, z_bi)
166 }
167
168 pub fn step_n_approx(
188 &self,
189 input_bd: Tensor<2>,
190 n: usize,
191 cache: Option<Mamba3Cache>,
192 ) -> (Tensor<2>, Mamba3Cache) {
193 assert!(n >= 1, "step_n_approx requires at least one step");
194 let keep_single = !matches!(cache, Some(Mamba3Cache::DoubleSsd(_)));
195 let cache = cache.map(|c| match c {
196 Mamba3Cache::DoubleSsd(c) => c,
197 Mamba3Cache::SingleSsd(c) => c.into(),
198 });
199
200 let (out, cache) = self.step_double_ssd(input_bd.clone(), cache);
203 let (out, cache) = if n == 1 {
204 (out, cache)
205 } else {
206 self.jump_constant(self.step_project(input_bd), n - 1, cache)
207 };
208
209 let cache = if keep_single {
210 Mamba3Cache::SingleSsd(cache.into())
211 } else {
212 Mamba3Cache::DoubleSsd(cache)
213 };
214 (out, cache)
215 }
216
217 fn jump_constant(
221 &self,
222 p: StepProjection,
223 k: usize,
224 cache: Mamba3DoubleSsdCache,
225 ) -> (Tensor<2>, Mamba3DoubleSsdCache) {
226 let StepProjection {
227 z_bi,
228 x_bhp,
229 b_bmhr,
230 c_bmhr,
231 rot_ba,
232 dt_bh,
233 da_bh,
234 alpha_bh,
235 beta_bh,
236 gamma_bh,
237 } = p;
238 let [batch, mimo_rank, nheads, _state_rank] = b_bmhr.dims();
239 let kf = k as f32;
240 let eps = div_eps(alpha_bh.dtype());
241
242 let alpha_k_bh = (da_bh * kf).exp();
244 let tail_bh = (beta_bh.clone() + gamma_bh.clone())
246 * (-alpha_k_bh.clone() + 1.0)
247 / (-alpha_bh.clone() + 1.0).clamp_min(eps);
248
249 let (b_drv_bmhr, b_n_bmhr, c_n_bmhr, rotation_n) = match self.rotation_kind() {
252 RotationKind::Complex2D => {
253 let angle1_bha = cache.rotation.clone().angle(); let theta_bha = per_step_angle(rot_ba, dt_bh); let num_rope_angles = theta_bha.dims()[2];
256 let rotate_pairwise = mimo_rank == 1;
257
258 let angle_n_bha = wrap_angle(angle1_bha.clone() + theta_bha.clone() * kf);
260 let expand_m = |a_bha: Tensor<3>| {
261 a_bha.unsqueeze_dim::<4>(1).expand([
262 batch,
263 mimo_rank,
264 nheads,
265 num_rope_angles,
266 ])
267 };
268 let b_n = apply_rope_partial::<4>(
269 b_bmhr.clone(),
270 expand_m(angle_n_bha.clone()),
271 self.rope_dim,
272 rotate_pairwise,
273 );
274 let c_n = apply_rope_partial::<4>(
275 c_bmhr,
276 expand_m(angle_n_bha.clone()),
277 self.rope_dim,
278 rotate_pairwise,
279 );
280
281 let (cos1, sin1) = cos_sin(theta_bha.clone());
283 let (cos_k, sin_k) = cos_sin(theta_bha.clone() * kf);
284 let (lead_re, lead_im) = cos_sin(angle1_bha + theta_bha * (kf - 1.0));
285 let a_bh1 = alpha_bh.clone().unsqueeze_dim::<3>(2);
286 let ak_bh1 = alpha_k_bh.clone().unsqueeze_dim::<3>(2);
287 let beta_bh1 = beta_bh.clone().unsqueeze_dim::<3>(2);
288 let gamma_bh1 = gamma_bh.clone().unsqueeze_dim::<3>(2);
289 let num_re = -ak_bh1.clone() * cos_k + 1.0;
290 let num_im = ak_bh1 * sin_k;
291 let den_re = -a_bh1.clone() * cos1.clone() + 1.0;
292 let den_im = a_bh1 * sin1.clone();
293 let (rat_re, rat_im) = complex_div(num_re, num_im, den_re, den_im);
294 let bg_re = beta_bh1 + gamma_bh1.clone() * cos1;
295 let bg_im = gamma_bh1 * sin1;
296 let (t_re, t_im) = complex_mul(rat_re, rat_im, bg_re, bg_im);
297 let (phi_re, phi_im) = complex_mul(t_re, t_im, lead_re, lead_im);
298
299 let b_drv = mul_complex_partial(
300 b_bmhr,
301 phi_re,
302 phi_im,
303 tail_bh,
304 self.rope_dim,
305 rotate_pairwise,
306 );
307 (b_drv, b_n, c_n, RotationState::Angle(angle_n_bha))
308 }
309 RotationKind::Quaternion4D => {
310 let q1_bhj4 = cache.rotation.clone().quaternion(); let blocks = self.num_quat_blocks;
312 let rope_width = blocks * 4;
313 let g_bhj3 = per_step_generator(rot_ba, dt_bh, blocks);
314
315 let q_pow_k = quat_pow(g_bhj3.clone(), kf);
317 let q_n_bhj4 = quat_normalize(quat_mul(q_pow_k.clone(), q1_bhj4.clone()));
318 let conj_qn_bmhj4 = quat_conj(q_n_bhj4.clone())
319 .unsqueeze_dim::<5>(1)
320 .expand([batch, mimo_rank, nheads, blocks, 4]);
321 let b_n = crate::mamba3::rotation::rotate_blocks_partial::<4, 5>(
322 b_bmhr.clone(),
323 conj_qn_bmhj4.clone(),
324 rope_width,
325 );
326 let c_n = crate::mamba3::rotation::rotate_blocks_partial::<4, 5>(
327 c_bmhr,
328 conj_qn_bmhj4,
329 rope_width,
330 );
331
332 let q_bhj4 = quat_from_scaled_axis::<4>(g_bhj3.clone());
335 let w_bhj4 = quat_conj(q_bhj4.clone());
336 let w_pow_km1 = quat_pow(-g_bhj3, kf - 1.0);
337 let alpha_bh11 = alpha_bh.clone().unsqueeze_dims::<4>(&[2, 3]);
338 let alpha_k_bh11 = alpha_k_bh.clone().unsqueeze_dims::<4>(&[2, 3]);
339 let beta_bh11 = beta_bh.clone().unsqueeze_dims::<4>(&[2, 3]);
340 let gamma_bh11 = gamma_bh.clone().unsqueeze_dims::<4>(&[2, 3]);
341 let num = quat_one_minus(q_pow_k * alpha_k_bh11);
342 let den_inv = quat_inv(quat_one_minus(q_bhj4 * alpha_bh11));
343 let bg = quat_scalar_affine(beta_bh11, gamma_bh11, w_bhj4);
344 let f_bhj4 = quat_mul(
345 quat_conj(q1_bhj4),
346 quat_mul(w_pow_km1, quat_mul(num, quat_mul(den_inv, bg))),
347 );
348
349 let b_drv = mul_quat_partial(b_bmhr, f_bhj4, tail_bh, rope_width);
350 (b_drv, b_n, c_n, RotationState::Quaternion(q_n_bhj4))
351 }
352 };
353 san(&b_drv_bmhr);
354 san(&b_n_bmhr);
355 san(&c_n_bmhr);
356 rotation_n.sanity();
357
358 let mimo_x_hmp = self.mimo_x_hmp.as_ref().map(|p| p.val());
360 let x_vals_bmhp =
361 helpers::build_v_with_mimo::<3, 4>(x_bhp.clone(), mimo_x_hmp.as_ref(), 1);
362 let xb_bhpr = {
363 let b_bhmr = b_drv_bmhr.permute([0, 2, 1, 3]);
364 let x_bhpm = x_vals_bmhp.clone().permute([0, 2, 3, 1]);
365 x_bhpm.matmul(b_bhmr)
366 };
367 let alpha_k_bh11 = alpha_k_bh.unsqueeze_dims::<4>(&[2, 3]);
368 let state_n_bhpr = alpha_k_bh11 * cache.ssm_bhpr + xb_bhpr;
369 san(&state_n_bhpr);
370
371 let out_m_bmhp = Self::step_readout(state_n_bhpr.clone(), c_n_bmhr);
373 san(&out_m_bmhp);
374 let out_bm = self.step_finish(out_m_bmhp, x_vals_bmhp, z_bi);
375
376 let cache = Mamba3DoubleSsdCache {
377 ssm_bhpr: state_n_bhpr,
378 k_state_bmhr: b_n_bmhr,
379 v_state_bhp: x_bhp,
380 rotation: rotation_n,
381 };
382 (out_bm, cache)
383 }
384}
385
386fn per_step_angle(rot_ba: Tensor<2>, dt_bh: Tensor<2>) -> Tensor<3> {
392 dt_bh.unsqueeze_dim::<3>(2) * (rot_ba.tanh() * PI).unsqueeze_dim::<3>(1)
393}
394
395fn per_step_generator(rot_ba: Tensor<2>, dt_bh: Tensor<2>, blocks: usize) -> Tensor<4> {
398 let [batch, _a] = rot_ba.dims();
399 (rot_ba.tanh() * PI)
400 .reshape([batch, blocks, 3])
401 .unsqueeze_dim::<4>(1)
402 * dt_bh.unsqueeze_dim::<3>(2).unsqueeze_dim::<4>(3)
403}
404
405fn cos_sin(angle_bha: Tensor<3>) -> (Tensor<3>, Tensor<3>) {
411 let a = wrap_angle(angle_bha);
412 (a.clone().cos(), a.sin())
413}
414
415fn complex_mul(
417 ar: Tensor<3>,
418 ai: Tensor<3>,
419 br: Tensor<3>,
420 bi: Tensor<3>,
421) -> (Tensor<3>, Tensor<3>) {
422 (
423 ar.clone() * br.clone() - ai.clone() * bi.clone(),
424 ai * br + ar * bi,
425 )
426}
427
428fn complex_div(
430 nr: Tensor<3>,
431 ni: Tensor<3>,
432 dr: Tensor<3>,
433 di: Tensor<3>,
434) -> (Tensor<3>, Tensor<3>) {
435 let eps = div_eps(dr.dtype());
436 let d2 = (dr.clone() * dr.clone() + di.clone() * di.clone()).clamp_min(eps);
437 (
438 (nr.clone() * dr.clone() + ni.clone() * di.clone()) / d2.clone(),
439 (ni * dr - nr * di) / d2,
440 )
441}
442
443fn mul_complex_partial(
450 x_bmhr: Tensor<4>,
451 re_bha: Tensor<3>,
452 im_bha: Tensor<3>,
453 tail_bh: Tensor<2>,
454 rope_dim: usize,
455 rotate_pairwise: bool,
456) -> Tensor<4> {
457 let [batch, mimo_rank, nheads, state_rank] = x_bmhr.dims();
458 let tail_b1h1 = tail_bh.unsqueeze_dims::<4>(&[1, 3]);
459 if rope_dim == 0 {
460 return x_bmhr * tail_b1h1;
462 }
463 let re_b1ha = re_bha.unsqueeze_dim::<4>(1);
464 let im_b1ha = im_bha.unsqueeze_dim::<4>(1);
465
466 if rotate_pairwise {
467 let n2 = rope_dim / 2;
470 let head = x_bmhr.clone().narrow(3, 0, rope_dim);
471 let head_pairs = head.reshape([batch, mimo_rank, nheads, n2, 2]);
472 let x0 = head_pairs.clone().narrow(4, 0, 1).squeeze_dim::<4>(4);
473 let x1 = head_pairs.narrow(4, 1, 1).squeeze_dim::<4>(4);
474 let x0m = re_b1ha.clone() * x0.clone() - im_b1ha.clone() * x1.clone();
475 let x1m = im_b1ha * x0 + re_b1ha * x1;
476 let head = Tensor::cat(
477 vec![x0m.unsqueeze_dim::<5>(4), x1m.unsqueeze_dim::<5>(4)],
478 4,
479 )
480 .reshape([batch, mimo_rank, nheads, rope_dim]);
481 if rope_dim == state_rank {
482 head
483 } else {
484 let tail = x_bmhr.narrow(3, rope_dim, state_rank - rope_dim) * tail_b1h1;
485 Tensor::cat(vec![head, tail], 3)
486 }
487 } else {
488 let half = state_rank / 2;
491 let active = rope_dim / 2;
492 let x_h1 = x_bmhr.clone().narrow(3, 0, half);
493 let x_h2 = x_bmhr.narrow(3, half, half);
494 let x_h1_rope = x_h1.clone().narrow(3, 0, active);
495 let x_h2_rope = x_h2.clone().narrow(3, 0, active);
496 let h1_m = re_b1ha.clone() * x_h1_rope.clone() - im_b1ha.clone() * x_h2_rope.clone();
497 let h2_m = im_b1ha * x_h1_rope + re_b1ha * x_h2_rope;
498 if active == half {
499 Tensor::cat(vec![h1_m, h2_m], 3)
500 } else {
501 let h1_pass = x_h1.narrow(3, active, half - active) * tail_b1h1.clone();
502 let h2_pass = x_h2.narrow(3, active, half - active) * tail_b1h1;
503 Tensor::cat(vec![h1_m, h1_pass, h2_m, h2_pass], 3)
504 }
505 }
506}
507
508fn quat_pow(g_bhj3: Tensor<4>, k: f32) -> Tensor<4> {
517 let eps = div_eps(g_bhj3.dtype());
518 let angle = (g_bhj3.clone() * g_bhj3.clone())
519 .sum_dim(3)
520 .clamp_min(eps)
521 .sqrt(); let half = wrap_angle(angle.clone() * (0.5 * k));
523 let w = half.clone().cos();
524 let v = g_bhj3 * (half.sin() / angle);
525 quat_normalize(Tensor::cat(vec![w, v], 3))
526}
527
528fn quat_one_minus(sq_bhj4: Tensor<4>) -> Tensor<4> {
531 let w = -sq_bhj4.clone().narrow(3, 0, 1) + 1.0;
532 let xyz = -sq_bhj4.narrow(3, 1, 3);
533 Tensor::cat(vec![w, xyz], 3)
534}
535
536fn quat_scalar_affine(a_bh11: Tensor<4>, b_bh11: Tensor<4>, q_bhj4: Tensor<4>) -> Tensor<4> {
539 let w = a_bh11 + b_bh11.clone() * q_bhj4.clone().narrow(3, 0, 1);
540 let xyz = b_bh11 * q_bhj4.narrow(3, 1, 3);
541 Tensor::cat(vec![w, xyz], 3)
542}
543
544fn quat_inv(q_bhj4: Tensor<4>) -> Tensor<4> {
547 let eps = div_eps(q_bhj4.dtype());
548 let n2 = (q_bhj4.clone() * q_bhj4.clone()).sum_dim(3).clamp_min(eps);
549 quat_conj(q_bhj4) / n2
550}
551
552fn mul_quat_partial(
557 x_bmhr: Tensor<4>,
558 f_bhj4: Tensor<4>,
559 tail_bh: Tensor<2>,
560 rope_width: usize,
561) -> Tensor<4> {
562 let [batch, mimo_rank, nheads, state_rank] = x_bmhr.dims();
563 let blocks = f_bhj4.dims()[2];
564 let tail_b1h1 = tail_bh.unsqueeze_dims::<4>(&[1, 3]);
565 if rope_width == 0 {
566 return x_bmhr * tail_b1h1;
567 }
568 let f_bmhj4 = f_bhj4
569 .unsqueeze_dim::<5>(1)
570 .expand([batch, mimo_rank, nheads, blocks, 4]);
571 if rope_width == state_rank {
572 rotate_state_rank_blocks::<4, 5>(x_bmhr, f_bmhj4)
573 } else {
574 let head = rotate_state_rank_blocks::<4, 5>(
575 x_bmhr.clone().narrow(3, 0, rope_width),
576 f_bmhj4,
577 );
578 let tail = x_bmhr.narrow(3, rope_width, state_rank - rope_width) * tail_b1h1;
579 Tensor::cat(vec![head, tail], 3)
580 }
581}
582
583#[cfg(all(test, feature = "_dev-test"))]
588mod tests;