use burn::tensor::{Tensor, backend::Backend};
use crate::precision::{to_f32, to_float};
pub fn rms_norm<B: Backend, const D: usize>(
x: Tensor<B, D>,
weight: Tensor<B, 1>,
eps: f64,
) -> Tensor<B, D> {
let out_dtype = x.dtype();
let dims = x.dims();
let hidden = dims[D - 1];
let xf = to_f32(x);
let mean_sq = xf.clone().powf_scalar(2.0).mean_dim(D - 1);
let inv_rms = mean_sq.add_scalar(eps).sqrt().recip();
let mut shape = [1usize; D];
shape[D - 1] = hidden;
let y = xf * inv_rms * to_f32(weight).reshape(shape);
to_float(y, out_dtype)
}
pub fn gemma_rms_norm<B: Backend, const D: usize>(
x: Tensor<B, D>,
weight: Tensor<B, 1>,
eps: f64,
) -> Tensor<B, D> {
rms_norm(x, weight.add_scalar(1.0), eps)
}
pub fn layer_norm<B: Backend, const D: usize>(
x: Tensor<B, D>,
weight: Tensor<B, 1>,
bias: Tensor<B, 1>,
eps: f64,
) -> Tensor<B, D> {
let dims = x.dims();
let hidden = dims[D - 1];
let out_dtype = x.dtype();
let xf = to_f32(x);
let mean = xf.clone().mean_dim(D - 1);
let centered = xf - mean;
let var = centered.clone().powf_scalar(2.0).mean_dim(D - 1);
let inv_std = var.add_scalar(eps).sqrt().recip();
let mut shape = [1usize; D];
shape[D - 1] = hidden;
let y = centered * inv_std * to_f32(weight).reshape(shape.clone())
+ to_f32(bias).reshape(shape);
to_float(y, out_dtype)
}
#[cfg(test)]
mod tests {
use super::*;
use burn::tensor::TensorData;
type TestBackend = burn::backend::NdArray<f32>;
#[test]
fn normalizes_rows_to_unit_rms() {
let device = burn::tensor::Device::<TestBackend>::default();
let x: Tensor<TestBackend, 2> = Tensor::from_data(
TensorData::new(vec![3.0f32, 4.0, 1.0, -2.0, 0.5, 2.5], [2, 3]),
&device,
);
let w: Tensor<TestBackend, 1> = Tensor::ones([3], &device);
let y = rms_norm(x, w, 1e-6);
let rms = y
.powf_scalar(2.0)
.mean_dim(1)
.sqrt()
.into_data()
.to_vec::<f32>()
.unwrap();
for (i, r) in rms.iter().enumerate() {
assert!((r - 1.0).abs() < 1e-4, "row {i} rms = {r}");
}
}
#[test]
fn applies_weight() {
let device = burn::tensor::Device::<TestBackend>::default();
let x: Tensor<TestBackend, 2> =
Tensor::from_data(TensorData::new(vec![1.0f32, 2.0], [1, 2]), &device);
let w: Tensor<TestBackend, 1> =
Tensor::from_data(TensorData::new(vec![2.0f32, 2.0], [2]), &device);
let y = rms_norm(x.clone(), w, 1e-6);
let z = rms_norm(x, Tensor::ones([2], &device), 1e-6);
let yv: Vec<f32> = y.into_data().to_vec().unwrap();
let zv: Vec<f32> = z.into_data().to_vec().unwrap();
for i in 0..2 {
assert!((yv[i] - 2.0 * zv[i]).abs() < 1e-4);
}
}
}