use burn::prelude::*;
use burn::nn::{Linear, LinearConfig};
use burn::tensor::activation::softmax;
use crate::model::rope::apply_rope;
#[derive(Module, Debug)]
pub struct RotarySelfAttention<B: Backend> {
pub qkv: Linear<B>,
pub proj: Linear<B>,
pub n_heads: usize,
pub head_dim: usize,
}
impl<B: Backend> RotarySelfAttention<B> {
pub fn new(
dim: usize,
n_heads: usize,
qkv_bias: bool,
device: &B::Device,
) -> Self {
let head_dim = dim / n_heads;
Self {
qkv: LinearConfig::new(dim, dim * 3).with_bias(qkv_bias).init(device),
proj: LinearConfig::new(dim, dim).with_bias(true).init(device),
n_heads,
head_dim,
}
}
pub fn forward(
&self,
x: Tensor<B, 3>,
freqs: Tensor<B, 4>,
) -> Tensor<B, 3> {
let [b, s, _] = x.dims();
let (h, dh) = (self.n_heads, self.head_dim);
let qkv = self.qkv.forward(x)
.reshape([b, s, 3, h, dh])
.swap_dims(0, 2);
let q = qkv.clone().narrow(0, 0, 1).reshape([b, s, h, dh]);
let k = qkv.clone().narrow(0, 1, 1).reshape([b, s, h, dh]);
let v = qkv.narrow(0, 2, 1).reshape([b, s, h, dh]);
let (q, k) = apply_rope(q, k, freqs);
let q = q.swap_dims(1, 2);
let k = k.swap_dims(1, 2);
let v = v.swap_dims(1, 2);
let scale = (dh as f64).powf(-0.5) as f32;
let attn = softmax(q.matmul(k.transpose()).mul_scalar(scale), 3);
let out = attn.matmul(v);
let out = out.swap_dims(1, 2).reshape([b, s, h * dh]);
self.proj.forward(out)
}
}