use candle_core::{Result, Tensor, DType};
use candle_nn::{VarBuilder, Linear, linear_no_bias, Conv1d, Conv1dConfig, Module};
use crate::config::ModelConfig;
const HIDDEN_STATE_CLAMP_MAX: f32 = 30.0;
pub struct MambaBlock {
in_proj: Linear,
conv1d: Conv1d,
x_proj: Linear,
dt_proj: Linear,
a_log: Tensor, d: Tensor, out_proj: Linear,
#[allow(dead_code)] d_inner: usize,
d_state: usize,
#[allow(dead_code)] d_conv: usize,
}
impl MambaBlock {
pub fn new(cfg: &ModelConfig, vb: VarBuilder) -> Result<Self> {
let d_model = cfg.hidden_size;
let d_inner = d_model * 2; let d_state = 16; let d_conv = 4;
let in_proj = linear_no_bias(d_model, d_inner * 2, vb.pp("in_proj"))?;
let conv1d = candle_nn::conv1d(
d_inner,
d_inner,
d_conv,
Conv1dConfig {
groups: d_inner, padding: d_conv - 1,
..Default::default()
},
vb.pp("conv1d")
)?;
let x_proj = linear_no_bias(d_inner, d_state + d_state + d_inner, vb.pp("x_proj"))?;
let dt_proj = candle_nn::linear(d_inner, d_inner, vb.pp("dt_proj"))?;
let a_log = vb.get((d_inner, d_state), "a_log")?;
let d = vb.get(d_inner, "d")?;
let out_proj = linear_no_bias(d_inner, d_model, vb.pp("out_proj"))?;
Ok(Self {
in_proj,
conv1d,
x_proj,
dt_proj,
a_log,
d,
out_proj,
d_inner,
d_state,
d_conv,
})
}
pub fn forward(&self, x: &Tensor) -> Result<Tensor> {
let (_b_sz, seq_len, _d_model) = x.dims3()?;
let x_and_res = self.in_proj.forward(x)?; let d_inner_2 = x_and_res.dim(2)? / 2;
let x = x_and_res.narrow(2, 0, d_inner_2)?.contiguous()?; let res = x_and_res.narrow(2, d_inner_2, d_inner_2)?.contiguous()?;
let x_t = x.transpose(1, 2)?.contiguous()?; let x_conv = self.conv1d.forward(&x_t)?;
let x_conv = x_conv.narrow(2, 0, seq_len)?; let x_conv = x_conv.transpose(1, 2)?.contiguous()?;
let x_conv = candle_nn::ops::silu(&x_conv)?;
let y = self.ssm(&x_conv)?;
let y = (y * candle_nn::ops::silu(&res)?)?;
self.out_proj.forward(&y)
}
fn ssm(&self, x: &Tensor) -> Result<Tensor> {
let (_b_sz, _seq_len, d_inner) = x.dims3()?;
let x_dbl = self.x_proj.forward(x)?.contiguous()?;
let delta = x_dbl.narrow(2, 0, d_inner)?.contiguous()?; let b = x_dbl.narrow(2, d_inner, self.d_state)?.contiguous()?; let c = x_dbl.narrow(2, d_inner + self.d_state, self.d_state)?.contiguous()?;
let delta = self.dt_proj.forward(&delta)?; let delta = (delta.exp()? + 1.0)?.log()?;
let a = self.a_log.neg()?.exp()?;
let y = self.selective_scan_sequential(x, &delta, &a, &b, &c)?;
let y = (y + x.broadcast_mul(&self.d)?)?;
Ok(y)
}
fn selective_scan_sequential(
&self,
u: &Tensor, delta: &Tensor, a: &Tensor, b: &Tensor, c: &Tensor, ) -> Result<Tensor> {
let (b_sz, seq_len, d_inner) = u.dims3()?;
let d_state = self.d_state;
let u = u.to_dtype(DType::F32)?;
let delta = delta.to_dtype(DType::F32)?;
let a = a.to_dtype(DType::F32)?;
let b = b.to_dtype(DType::F32)?;
let c = c.to_dtype(DType::F32)?;
let mut outputs = Vec::new();
for batch_idx in 0..b_sz {
let mut h = Tensor::zeros((d_inner, d_state), DType::F32, u.device())?;
let mut batch_outputs = Vec::new();
for t in 0..seq_len {
let u_t = u.narrow(0, batch_idx, 1)?.narrow(1, t, 1)?.squeeze(0)?.squeeze(0)?;
let delta_t = delta.narrow(0, batch_idx, 1)?.narrow(1, t, 1)?.squeeze(0)?.squeeze(0)?;
let b_t = b.narrow(0, batch_idx, 1)?.narrow(1, t, 1)?.squeeze(0)?.squeeze(0)?;
let c_t = c.narrow(0, batch_idx, 1)?.narrow(1, t, 1)?.squeeze(0)?.squeeze(0)?;
let delta_a = (delta_t.unsqueeze(1)?.broadcast_as((d_inner, d_state))? * &a)?;
let delta_a = delta_a.exp()?;
let delta_b_u = (delta_t.unsqueeze(1)? * u_t.unsqueeze(1)?)?
.broadcast_mul(&b_t.unsqueeze(0)?)?;
h = ((delta_a * &h)? + delta_b_u)?;
h = h.clamp(-HIDDEN_STATE_CLAMP_MAX as f64, HIDDEN_STATE_CLAMP_MAX as f64)?;
let y_t = h.matmul(&c_t.unsqueeze(1)?)?; let y_t = y_t.reshape((1, d_inner))?;
batch_outputs.push(y_t);
}
let batch_out = Tensor::cat(&batch_outputs, 0)?; outputs.push(batch_out.unsqueeze(0)?);
}
let output = Tensor::cat(&outputs, 0)?;
Ok(output)
}
}