1use burn::tensor::{activation, Tensor};
2
3pub fn burn_attention(
6 q: Tensor<4>,
7 k: Tensor<4>,
8 v: Tensor<4>,
9 scale: f32,
10 mask: Option<Tensor<4>>,
11) -> Tensor<4> {
12 let scores = q.matmul(k.permute([0, 1, 3, 2])).mul_scalar(scale);
13 let scores = match mask {
14 Some(m) => scores + m,
15 None => scores,
16 };
17 let probs = activation::softmax(scores, 3);
18 probs.matmul(v)
19}
20
21#[cfg(test)]
22mod tests {
23 use super::*;
24 use burn::tensor::{Device, TensorData};
25
26 #[test]
27 fn uniform_attention_averages_values() {
28 let device = Device::flex();
29 let q = Tensor::<4>::zeros([1, 1, 2, 1], &device);
30 let k = Tensor::<4>::zeros([1, 1, 2, 1], &device);
31 let v = Tensor::<4>::from_data(TensorData::from([[[[2.0f32], [4.0]]]]), &device);
32 let out = burn_attention(q, k, v, 1.0, None);
33 let vals: Vec<f32> = out.to_data().try_to_vec().unwrap();
34 assert!((vals[0] - 3.0).abs() < 1e-5);
35 assert!((vals[1] - 3.0).abs() < 1e-5);
36 }
37}