use crate::config::MambaConfig;
use crate::inference::{MambaStepScratch, mamba_step, mamba_step_no_proj};
use crate::state::MambaState;
use crate::weights::{MambaLayerWeights, MambaWeights};
pub struct MambaBackbone {
weights: MambaWeights,
cfg: MambaConfig,
input_dim: usize,
identity_proj: bool,
}
impl MambaBackbone {
pub fn init(cfg: MambaConfig, input_dim: usize, seed: u64) -> Self {
let weights = MambaWeights::init(&cfg, input_dim, seed);
Self {
weights,
cfg,
input_dim,
identity_proj: false,
}
}
pub fn from_weights(cfg: MambaConfig, mut weights: MambaWeights) -> Result<Self, String> {
let input_dim = weights.input_proj_w.len() / cfg.d_model;
weights.validate(&cfg, input_dim)?;
for lw in &mut weights.layers {
lw.compute_a_neg();
}
Ok(Self {
weights,
cfg,
input_dim,
identity_proj: false,
})
}
#[cfg(feature = "hf")]
pub fn from_weights_no_proj(
cfg: MambaConfig,
mut weights: MambaWeights,
) -> Result<Self, String> {
if weights.layers.len() != cfg.n_layers {
return Err(format!(
"expected {} layers, got {}",
cfg.n_layers,
weights.layers.len()
));
}
let d = cfg.d_model;
let di = cfg.d_inner();
let ds = cfg.d_state;
let dc = cfg.d_conv;
let dr = cfg.dt_rank();
let xd = cfg.xdbl_dim();
for (i, lw) in weights.layers.iter().enumerate() {
let check = |name: &str, actual: usize, expected: usize| -> Result<(), String> {
if actual != expected {
return Err(format!(
"layer[{i}].{name}: expected {expected}, got {actual}"
));
}
Ok(())
};
check("norm_weight", lw.norm_weight.len(), d)?;
check("in_proj_w", lw.in_proj_w.len(), d * 2 * di)?;
check("conv1d_weight", lw.conv1d_weight.len(), di * dc)?;
check("conv1d_bias", lw.conv1d_bias.len(), di)?;
check("x_proj_w", lw.x_proj_w.len(), di * xd)?;
check("dt_proj_w", lw.dt_proj_w.len(), dr * di)?;
check("dt_proj_b", lw.dt_proj_b.len(), di)?;
check("a_log", lw.a_log.len(), di * ds)?;
check("d_param", lw.d_param.len(), di)?;
check("out_proj_w", lw.out_proj_w.len(), di * d)?;
}
if weights.norm_f_weight.len() != d {
return Err(format!(
"norm_f_weight: expected {d}, got {}",
weights.norm_f_weight.len()
));
}
for lw in &mut weights.layers {
lw.compute_a_neg();
}
Ok(Self {
weights,
cfg,
input_dim: d,
identity_proj: true,
})
}
pub fn into_weights(self) -> MambaWeights {
self.weights
}
pub fn weights(&self) -> &MambaWeights {
&self.weights
}
pub fn weights_mut(&mut self) -> &mut MambaWeights {
&mut self.weights
}
pub fn layer(&self, index: usize) -> &MambaLayerWeights {
&self.weights.layers[index]
}
pub fn layer_mut(&mut self, index: usize) -> &mut MambaLayerWeights {
&mut self.weights.layers[index]
}
pub fn n_layers(&self) -> usize {
self.cfg.n_layers
}
pub fn param_count(&self) -> usize {
self.weights.param_count(self.input_dim, &self.cfg)
}
pub fn config(&self) -> &MambaConfig {
&self.cfg
}
pub fn input_dim(&self) -> usize {
self.input_dim
}
pub fn forward_step(
&self,
input: &[f32],
output: &mut [f32],
state: &mut MambaState,
scratch: &mut MambaStepScratch,
) {
if self.identity_proj {
mamba_step_no_proj(
input,
output,
&self.weights,
&mut state.layers,
scratch,
&self.cfg,
);
} else {
mamba_step(
input,
output,
&self.weights,
&mut state.layers,
scratch,
&self.cfg,
self.input_dim,
);
}
}
pub fn forward_sequence(
&self,
inputs: &[f32],
outputs: &mut [f32],
state: &mut MambaState,
scratch: &mut MambaStepScratch,
seq_len: usize,
) {
let dm = self.cfg.d_model;
debug_assert_eq!(inputs.len(), seq_len * self.input_dim);
debug_assert_eq!(outputs.len(), seq_len * dm);
for t in 0..seq_len {
let inp = &inputs[t * self.input_dim..(t + 1) * self.input_dim];
let out = &mut outputs[t * dm..(t + 1) * dm];
self.forward_step(inp, out, state, scratch);
}
}
pub fn forward_step_batch(
&self,
inputs: &[f32],
outputs: &mut [f32],
states: &mut [MambaState],
scratches: &mut [MambaStepScratch],
) {
crate::inference::mamba_step_batch(
inputs,
outputs,
&self.weights,
states,
scratches,
&self.cfg,
self.input_dim,
);
}
pub fn alloc_state(&self) -> MambaState {
MambaState::zeros(
self.cfg.n_layers,
self.cfg.d_inner(),
self.cfg.d_state,
self.cfg.d_conv,
)
}
pub fn alloc_scratch(&self) -> MambaStepScratch {
MambaStepScratch::new(&self.cfg)
}
pub fn alloc_prefill_scratch(
&self,
seq_len: usize,
) -> crate::mamba_ssm::cpu::prefill::PrefillScratch {
let dims = crate::ops::dims::MambaDims::from_config(&self.cfg, seq_len, self.input_dim);
crate::mamba_ssm::cpu::prefill::PrefillScratch::new(&dims)
}
pub fn forward_prefill(
&self,
inputs: &[f32],
outputs: &mut [f32],
state: &mut MambaState,
scratch: &mut crate::mamba_ssm::cpu::prefill::PrefillScratch,
seq_len: usize,
mode: crate::mamba_ssm::cpu::prefill::PrefillMode,
) {
let dims = crate::ops::dims::MambaDims::from_config(&self.cfg, seq_len, self.input_dim);
crate::mamba_ssm::cpu::prefill::forward_mamba_backbone_prefill_mode(
outputs,
inputs,
&self.weights,
state,
scratch,
&dims,
mode,
);
}
}