use super::weights::TrainMambaWeights;
use crate::ops::blas::matvec_forward;
use crate::ops::fast_math::{RMS_NORM_EPS, fast_exp_scalar};
pub struct MambaTargetScratch {
pub proj: Vec<f32>,
pub x: Vec<f32>,
pub gate_silu: Vec<f32>,
pub norm_buf: Vec<f32>,
pub residual: Vec<f32>,
pub xdbl: Vec<f32>,
pub delta: Vec<f32>,
pub y: Vec<f32>,
pub h: Vec<f32>,
}
impl MambaTargetScratch {
pub fn new(d_model: usize, d_inner: usize, d_state: usize, dt_rank: usize) -> Self {
Self {
proj: vec![0.0; 2 * d_inner],
x: vec![0.0; d_inner],
gate_silu: vec![0.0; d_inner],
norm_buf: vec![0.0; d_model],
residual: vec![0.0; d_model],
xdbl: vec![0.0; dt_rank + 2 * d_state],
delta: vec![0.0; d_inner],
y: vec![0.0; d_inner],
h: vec![0.0; d_inner * d_state],
}
}
}
pub fn forward_mamba_target_step(
output: &mut [f32], input: &[f32], w: &TrainMambaWeights,
scratch: &mut MambaTargetScratch,
dims: (usize, usize, usize, usize, usize, usize), ) {
let (dm, di, ds, dc, dr, mid) = dims;
let xdbl_dim = dr + 2 * ds;
matvec_forward(
&mut output[..dm],
input,
&w.input_proj_w,
Some(&w.input_proj_b),
mid,
dm,
);
for lw in &w.layers {
scratch.residual[..dm].copy_from_slice(&output[..dm]);
let mean_sq: f32 = output[..dm].iter().map(|v| v * v).sum::<f32>() / dm as f32;
let inv_rms = 1.0 / (mean_sq + RMS_NORM_EPS).sqrt();
for ((nb, &o), &nw) in scratch.norm_buf[..dm]
.iter_mut()
.zip(output[..dm].iter())
.zip(lw.norm_weight[..dm].iter())
{
*nb = o * inv_rms * nw;
}
matvec_forward(
&mut scratch.proj[..2 * di],
&scratch.norm_buf[..dm],
&lw.in_proj_w,
None,
dm,
2 * di,
);
scratch.x[..di].copy_from_slice(&scratch.proj[..di]);
for (gs, &v) in scratch.gate_silu[..di]
.iter_mut()
.zip(scratch.proj[di..2 * di].iter())
{
let sig = 1.0 / (1.0 + fast_exp_scalar(-v));
*gs = v * sig;
}
for d in 0..di {
let val = lw.conv1d_bias[d] + scratch.x[d] * lw.conv1d_weight[d * dc + dc - 1];
let sig = 1.0 / (1.0 + fast_exp_scalar(-val));
scratch.x[d] = val * sig; }
matvec_forward(
&mut scratch.xdbl[..xdbl_dim],
&scratch.x[..di],
&lw.x_proj_w,
None,
di,
xdbl_dim,
);
matvec_forward(
&mut scratch.delta[..di],
&scratch.xdbl[..dr],
&lw.dt_proj_w,
Some(&lw.dt_proj_b),
dr,
di,
);
for d in 0..di {
let raw = scratch.delta[d];
scratch.delta[d] = if raw > 20.0 {
raw
} else {
(1.0 + fast_exp_scalar(raw)).ln()
};
}
scratch.h[..di * ds].fill(0.0);
let b_offset = dr;
let c_offset = dr + ds;
for d in 0..di {
let delta_d = scratch.delta[d];
let u_d = scratch.x[d];
let delta_u_d = delta_d * u_d;
let mut y_d = 0.0_f32;
for n in 0..ds {
let idx = d * ds + n;
let a_dn = -fast_exp_scalar(lw.a_log[idx]);
let da = fast_exp_scalar(delta_d * a_dn);
let b_n = scratch.xdbl[b_offset + n];
let c_n = scratch.xdbl[c_offset + n];
scratch.h[idx] = da * scratch.h[idx] + delta_u_d * b_n;
y_d += scratch.h[idx] * c_n;
}
y_d += lw.d_param[d] * u_d;
scratch.y[d] = y_d;
}
for d in 0..di {
scratch.y[d] *= scratch.gate_silu[d];
}
matvec_forward(
&mut output[..dm],
&scratch.y[..di],
&lw.out_proj_w,
None,
di,
dm,
);
for (o, &r) in output[..dm].iter_mut().zip(scratch.residual[..dm].iter()) {
*o += r;
}
}
let mean_sq: f32 = output[..dm].iter().map(|v| v * v).sum::<f32>() / dm as f32;
let inv_rms = 1.0 / (mean_sq + RMS_NORM_EPS).sqrt();
for (o, &nfw) in output[..dm].iter_mut().zip(w.norm_f_weight[..dm].iter()) {
*o *= inv_rms * nfw;
}
}
pub struct MambaTargetSeqScratch {
pub temporal: Vec<f32>,
pub proj: Vec<f32>,
pub x: Vec<f32>,
pub gate_silu: Vec<f32>,
pub norm_buf: Vec<f32>,
pub xdbl: Vec<f32>,
pub delta: Vec<f32>,
pub y: Vec<f32>,
pub residual: Vec<f32>,
pub conv_states: Vec<f32>,
pub ssm_states: Vec<f32>,
pub seq_len: usize,
}
impl MambaTargetSeqScratch {
pub fn new(
dm: usize,
di: usize,
ds: usize,
dc: usize,
dr: usize,
nl: usize,
seq_len: usize,
) -> Self {
Self {
temporal: vec![0.0; dm],
proj: vec![0.0; 2 * di],
x: vec![0.0; di],
gate_silu: vec![0.0; di],
norm_buf: vec![0.0; dm],
xdbl: vec![0.0; dr + 2 * ds],
delta: vec![0.0; di],
y: vec![0.0; di],
residual: vec![0.0; dm],
conv_states: vec![0.0; nl * di * dc],
ssm_states: vec![0.0; nl * di * ds],
seq_len,
}
}
pub fn reset_states(&mut self) {
self.conv_states.fill(0.0);
self.ssm_states.fill(0.0);
}
}
pub fn forward_mamba_target_sequence(
output: &mut [f32], ip_out_flat: &[f32], w: &TrainMambaWeights,
scratch: &mut MambaTargetSeqScratch,
dims: (usize, usize, usize, usize, usize, usize), ) {
let (dm, di, ds, dc, dr, seq_len) = dims;
let xdbl_dim = dr + 2 * ds;
let conv_per_layer = di * dc;
let ssm_per_layer = di * ds;
for t in 0..seq_len {
scratch.temporal[..dm].copy_from_slice(&ip_out_flat[t * dm..(t + 1) * dm]);
for (layer_idx, lw) in w.layers.iter().enumerate() {
let conv_start = layer_idx * conv_per_layer;
let ssm_start = layer_idx * ssm_per_layer;
scratch.residual[..dm].copy_from_slice(&scratch.temporal[..dm]);
let mean_sq: f32 =
scratch.temporal[..dm].iter().map(|v| v * v).sum::<f32>() / dm as f32;
let inv_rms = 1.0 / (mean_sq + RMS_NORM_EPS).sqrt();
for ((nb, &t), &nw) in scratch.norm_buf[..dm]
.iter_mut()
.zip(scratch.temporal[..dm].iter())
.zip(lw.norm_weight[..dm].iter())
{
*nb = t * inv_rms * nw;
}
matvec_forward(
&mut scratch.proj[..2 * di],
&scratch.norm_buf[..dm],
&lw.in_proj_w,
None,
dm,
2 * di,
);
scratch.x[..di].copy_from_slice(&scratch.proj[..di]);
for d in 0..di {
let v = scratch.proj[di + d];
let sig = 1.0 / (1.0 + fast_exp_scalar(-v));
scratch.gate_silu[d] = v * sig;
}
{
let cs = &mut scratch.conv_states[conv_start..conv_start + conv_per_layer];
for d in 0..di {
let base = d * dc;
for k in 0..dc - 1 {
cs[base + k] = cs[base + k + 1];
}
cs[base + dc - 1] = scratch.x[d];
let mut val = lw.conv1d_bias[d];
for k in 0..dc {
val += cs[base + k] * lw.conv1d_weight[base + k];
}
let sig = 1.0 / (1.0 + fast_exp_scalar(-val));
scratch.x[d] = val * sig;
}
}
matvec_forward(
&mut scratch.xdbl[..xdbl_dim],
&scratch.x[..di],
&lw.x_proj_w,
None,
di,
xdbl_dim,
);
matvec_forward(
&mut scratch.delta[..di],
&scratch.xdbl[..dr],
&lw.dt_proj_w,
Some(&lw.dt_proj_b),
dr,
di,
);
for d in 0..di {
let raw = scratch.delta[d];
scratch.delta[d] = if raw > 20.0 {
raw
} else {
(1.0 + fast_exp_scalar(raw)).ln()
};
}
let b_offset = dr;
let c_offset = dr + ds;
let h = &mut scratch.ssm_states[ssm_start..ssm_start + ssm_per_layer];
for d in 0..di {
let delta_d = scratch.delta[d];
let u_d = scratch.x[d];
let delta_u_d = delta_d * u_d;
let mut y_d = 0.0_f32;
for n in 0..ds {
let idx = d * ds + n;
let a_dn = -fast_exp_scalar(lw.a_log[idx]);
let da = fast_exp_scalar(delta_d * a_dn);
let b_n = scratch.xdbl[b_offset + n];
let c_n = scratch.xdbl[c_offset + n];
h[idx] = da * h[idx] + delta_u_d * b_n;
y_d += h[idx] * c_n;
}
y_d += lw.d_param[d] * u_d;
scratch.y[d] = y_d;
}
for d in 0..di {
scratch.y[d] *= scratch.gate_silu[d];
}
matvec_forward(
&mut scratch.temporal[..dm],
&scratch.y[..di],
&lw.out_proj_w,
None,
di,
dm,
);
for (t, &r) in scratch.temporal[..dm]
.iter_mut()
.zip(scratch.residual[..dm].iter())
{
*t += r;
}
}
}
let mean_sq: f32 = scratch.temporal[..dm].iter().map(|v| v * v).sum::<f32>() / dm as f32;
let inv_rms = 1.0 / (mean_sq + RMS_NORM_EPS).sqrt();
for (t, &nfw) in scratch.temporal[..dm]
.iter_mut()
.zip(w.norm_f_weight[..dm].iter())
{
*t *= inv_rms * nfw;
}
output[..dm].copy_from_slice(&scratch.temporal[..dm]);
}