use crate::tensor::Tensor;
use crate::buffer::Buffer;
use crate::errors::EtensorResult;
use crate::backends::traits::Backend;
pub struct CpuBackend;
impl Backend for CpuBackend {
fn add(a: &Tensor, b: &Tensor) -> EtensorResult<Tensor> {
let slice_a = a.data.as_f32_slice()?;
let slice_b = b.data.as_f32_slice()?;
let out_vec: Vec<f32> = slice_a.iter().zip(slice_b).map(|(x, y)| x + y).collect();
Ok(Tensor::new(
Buffer::from_f32_vec(out_vec),
a.shape.clone(),
a.device,
a.dtype,
false, ))
}
fn mul(a: &Tensor, b: &Tensor) -> EtensorResult<Tensor> {
let slice_a = a.data.as_f32_slice()?;
let slice_b = b.data.as_f32_slice()?;
let out_vec: Vec<f32> = slice_a.iter().zip(slice_b).map(|(x, y)| x * y).collect();
Ok(Tensor::new(
Buffer::from_f32_vec(out_vec),
a.shape.clone(),
a.device,
a.dtype,
false,
))
}
fn matmul(a: &Tensor, b: &Tensor) -> EtensorResult<Tensor> {
crate::backends::cpu::matmul::matmul_forward(a, b)
}
fn sum_all(a: &Tensor) -> EtensorResult<Tensor> {
crate::backends::cpu::reduce::sum_all(a)
}
fn mean_all(a: &Tensor) -> EtensorResult<Tensor> {
crate::backends::cpu::reduce::mean_all(a)
}
fn max_all(a: &Tensor) -> EtensorResult<Tensor> {
crate::backends::cpu::reduce::max_all(a)
}
fn relu(a: &Tensor) -> EtensorResult<Tensor> {
crate::backends::cpu::unary::relu_forward(a)
}
fn sigmoid(a: &Tensor) -> EtensorResult<Tensor> {
crate::backends::cpu::unary::sigmoid_forward(a)
}
fn add_relu(a: &Tensor, b: &Tensor) -> EtensorResult<Tensor> {
crate::backends::cpu::fusion::add_relu_forward(a, b)
}
fn linear(x: &Tensor, w: &Tensor, b: &Tensor) -> EtensorResult<Tensor> {
crate::backends::cpu::fusion::linear_forward(x, w, b)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::EtensorError;
use crate::device::Device;
use crate::dtypes::DType;
use crate::shape::Shape;
fn make_test_tensor(data: Vec<f32>) -> Tensor {
let len = data.len();
Tensor::new(
Buffer::from_f32_vec(data),
Shape::new(vec![len]),
Device::Cpu,
DType::F32,
false,
)
}
#[test]
fn test_cpu_backend_add() {
let a = make_test_tensor(vec![1.5, 2.5, 3.5]);
let b = make_test_tensor(vec![2.0, 3.0, 4.0]);
let c = CpuBackend::add(&a, &b).unwrap();
let slice = c.data.as_f32_slice().unwrap();
assert_eq!(slice, &[3.5, 5.5, 7.5]);
}
#[test]
fn test_cpu_backend_mul() {
let a = make_test_tensor(vec![2.0, 4.0, 6.0]);
let b = make_test_tensor(vec![3.0, 0.5, 10.0]);
let c = CpuBackend::mul(&a, &b).unwrap();
let slice = c.data.as_f32_slice().unwrap();
assert_eq!(slice, &[6.0, 2.0, 60.0]);
}
#[test]
fn test_cpu_backend_rejects_gpu_tensors() {
let shape = Shape::new(vec![1]);
let a = Tensor::new(
Buffer::new_cpu_zeros(1, DType::I32),
shape.clone(),
Device::Cpu,
DType::I32,
false
);
let b = Tensor::new(
Buffer::new_cpu_zeros(1, DType::I32),
shape,
Device::Cpu,
DType::I32,
false
);
let result = CpuBackend::add(&a, &b);
assert!(result.is_err());
if let Err(EtensorError::DTypeMismatch { .. }) = result {
} else {
panic!("Expected DTypeMismatch error from the CPU Kernel!");
}
}
}