brain2qwerty 0.0.1

Brain2Qwerty V1/V2 MEG neural decoding inference in Rust (parity-tested vs Python)
Documentation
//! SimpleConvTimeAgg keystroke encoder (V1).

use crate::model::conv_conformer::load_simple_conv;
use crate::model::simple_conv::SimpleConvEncoder;
use crate::tensor::Tensor;
use crate::v1::bahdanau::BahdanauAttention;
use crate::v1::config::V1EncoderConfig;
use crate::weights::{get, load_safetensors, param_to_tensor, WeightStore};

pub struct SimpleConvTimeAgg {
    pub conv: SimpleConvEncoder,
    pub attention: BahdanauAttention,
    pub hidden: usize,
}

impl SimpleConvTimeAgg {
    pub fn from_config_and_weights(
        cfg: &V1EncoderConfig,
        store: &mut WeightStore,
        prefix: &str,
    ) -> anyhow::Result<Self> {
        anyhow::ensure!(
            cfg.time_agg_out == "att",
            "only Bahdanau attention (time_agg_out=att) is supported"
        );
        let conv = load_simple_conv(&cfg.encoder_config, store, prefix)?;
        let att_prefix = format!("{prefix}time_agg_out.");
        let attention = BahdanauAttention {
            wa_w: param_to_tensor(get(store, &format!("{att_prefix}Wa.weight"))?),
            wa_b: param_to_tensor(get(store, &format!("{att_prefix}Wa.bias"))?),
            va_w: param_to_tensor(get(store, &format!("{att_prefix}Va.weight"))?),
            va_b: param_to_tensor(get(store, &format!("{att_prefix}Va.bias"))?),
        };
        Ok(Self {
            hidden: cfg.encoder_config.hidden,
            conv,
            attention,
        })
    }

    pub fn from_tiny_weights(weights_path: &str) -> anyhow::Result<Self> {
        let cfg = crate::v1::config::V1Config::tiny().brain_model_config;
        let mut store = load_safetensors(weights_path)?;
        Self::from_config_and_weights(&cfg, &mut store, "")
    }

    /// Input `x`: (B, C, T) MEG window; returns (B, hidden) keystroke embeddings.
    pub fn forward(&self, x: &Tensor, subject_ids: &[usize], chan_pos: Option<&Tensor>) -> Tensor {
        let z = self.conv.forward(x, subject_ids, chan_pos);
        let ctx = self.attention.forward(&z);
        assert_eq!(ctx.shape[0], x.shape[0]);
        assert_eq!(ctx.shape[1], self.hidden);
        ctx.reshape(&[ctx.shape[0], self.hidden])
    }
}