use crate::backend::BackendError;
use crate::backend::cpu::matmul::matmul_f32;
use crate::core::tensor::Tensor;
pub fn silu_f32(x: &Tensor) -> Result<Tensor, BackendError> {
let data = x.as_f32();
let out: Vec<f32> = data.iter().map(|&v| v / (1.0 + (-v).exp())).collect();
Ok(Tensor::from_f32(x.shape.clone(), out))
}
pub fn gelu_erf_f32(x: &Tensor) -> Result<Tensor, BackendError> {
let data = x.as_f32();
let sqrt_2 = std::f32::consts::SQRT_2;
let out: Vec<f32> = data
.iter()
.map(|&v| 0.5 * v * (1.0 + erf_approx(v / sqrt_2)))
.collect();
Ok(Tensor::from_f32(x.shape.clone(), out))
}
pub fn gelu_tanh_f32(x: &Tensor) -> Result<Tensor, BackendError> {
let data = x.as_f32();
let c = (2.0 / std::f32::consts::PI).sqrt();
let out: Vec<f32> = data
.iter()
.map(|&v| 0.5 * v * (1.0 + (c * (v + 0.044715 * v * v * v)).tanh()))
.collect();
Ok(Tensor::from_f32(x.shape.clone(), out))
}
pub fn swiglu_f32(
x: &Tensor,
w_gate: &Tensor,
w_up: &Tensor,
w_down: &Tensor,
) -> Result<Tensor, BackendError> {
let gate = matmul_f32(x, w_gate)?;
let up = matmul_f32(x, w_up)?;
let gate_silu = silu_f32(&gate)?;
let gd = gate_silu.as_f32();
let ud = up.as_f32();
assert_eq!(gd.len(), ud.len());
let mul: Vec<f32> = gd.iter().zip(ud.iter()).map(|(a, b)| a * b).collect();
let mid = Tensor::from_f32(gate_silu.shape.clone(), mul);
matmul_f32(&mid, w_down)
}
fn erf_approx(x: f32) -> f32 {
let a1 = 0.254_829_592;
let a2 = -0.284_496_736;
let a3 = 1.421_413_741;
let a4 = -1.453_152_027;
let a5 = 1.061_405_429;
let p = 0.327_591_1;
let sign = if x < 0.0 { -1.0 } else { 1.0 };
let x = x.abs();
let t = 1.0 / (1.0 + p * x);
let y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * (-x * x).exp();
sign * y
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn silu_zero_is_zero() {
let x = Tensor::from_f32(vec![1], vec![0.0]);
let y = silu_f32(&x).unwrap();
assert!((y.to_f32_vec()[0]).abs() < 1e-6);
}
#[test]
fn silu_one() {
let x = Tensor::from_f32(vec![1], vec![1.0]);
let y = silu_f32(&x).unwrap();
assert!((y.to_f32_vec()[0] - 0.731_059).abs() < 1e-5);
}
#[test]
fn gelu_erf_zero() {
let x = Tensor::from_f32(vec![1], vec![0.0]);
let y = gelu_erf_f32(&x).unwrap();
assert!((y.to_f32_vec()[0]).abs() < 1e-6);
}
#[test]
fn gelu_tanh_zero() {
let x = Tensor::from_f32(vec![1], vec![0.0]);
let y = gelu_tanh_f32(&x).unwrap();
assert!((y.to_f32_vec()[0]).abs() < 1e-6);
}
#[test]
fn gelu_variants_close_at_small_values() {
let x = Tensor::from_f32(vec![1], vec![0.5]);
let e = gelu_erf_f32(&x).unwrap().to_f32_vec()[0];
let t = gelu_tanh_f32(&x).unwrap().to_f32_vec()[0];
assert!((e - t).abs() < 1e-3);
}
}