use burn::prelude::*;
pub struct RotaryEmbedding<B: Backend> {
pub freqs_cis: Tensor<B, 4>,
pub max_seqlen: usize,
pub head_dim: usize,
}
impl<B: Backend> RotaryEmbedding<B> {
pub fn new(
head_dim: usize,
max_seqlen: usize,
theta: f64,
device: &B::Device,
) -> Self {
let half = head_dim / 2;
let mut table = vec![0f32; max_seqlen * half * 4];
for pos in 0..max_seqlen {
for h in 0..half {
let freq = 1.0 / theta.powf((2 * h) as f64 / head_dim as f64) as f32;
let angle = pos as f32 * freq;
let (s, c) = angle.sin_cos();
let base = (pos * half + h) * 4;
table[base] = c; table[base + 1] = -s; table[base + 2] = s; table[base + 3] = c; }
}
let freqs_cis = Tensor::<B, 1>::from_data(
TensorData::new(table, vec![max_seqlen * half * 4]),
device,
)
.reshape([max_seqlen, half, 2, 2]);
Self { freqs_cis, max_seqlen, head_dim }
}
pub fn get_freqs(&self, seq_len: usize) -> Tensor<B, 4> {
assert!(seq_len <= self.max_seqlen,
"seq_len {seq_len} > max_seqlen {}", self.max_seqlen);
self.freqs_cis.clone().narrow(0, 0, seq_len)
}
}
pub fn apply_rope<B: Backend>(
xq: Tensor<B, 4>,
xk: Tensor<B, 4>,
freqs: Tensor<B, 4>,
) -> (Tensor<B, 4>, Tensor<B, 4>) {
let [_b, s, h, d] = xq.dims();
let half = d / 2;
let cos = freqs
.clone()
.narrow(2, 0, 1)
.narrow(3, 0, 1)
.reshape([1, s, 1, half]);
let sin = freqs
.narrow(2, 1, 1)
.narrow(3, 0, 1)
.reshape([1, s, 1, half]);
(
rotate_half(xq, cos.clone(), sin.clone(), s, h, half),
rotate_half(xk, cos, sin, s, h, half),
)
}
fn rotate_half<B: Backend>(
x: Tensor<B, 4>,
cos: Tensor<B, 4>,
sin: Tensor<B, 4>,
s: usize,
h: usize,
half: usize,
) -> Tensor<B, 4> {
let b = x.dims()[0];
let pairs = x.reshape([b, s, h, half, 2]);
let even = pairs.clone().narrow(4, 0, 1).reshape([b, s, h, half]);
let odd = pairs.narrow(4, 1, 1).reshape([b, s, h, half]);
let out_even = even.clone() * cos.clone() - odd.clone() * sin.clone();
let out_odd = even * sin + odd * cos;
Tensor::stack::<5>(vec![out_even, out_odd], 4)
.reshape([b, s, h, half * 2])
}