use crate::tensor::Tensor;
pub trait InPlace {
fn add_inplace(&mut self, other: &Self);
fn sub_inplace(&mut self, other: &Self);
fn mul_elementwise_inplace(&mut self, other: &Self);
fn mul_scalar_inplace(&mut self, scalar: f64);
fn div_scalar_inplace(&mut self, scalar: f64);
fn relu_inplace(&mut self);
fn leaky_relu_inplace(&mut self, alpha: f64);
fn sigmoid_inplace(&mut self);
fn tanh_inplace(&mut self);
fn clamp_inplace(&mut self, min: f64, max: f64);
fn zero_inplace(&mut self);
fn fill_inplace(&mut self, value: f64);
}
impl<const N: usize> InPlace for Tensor<N> {
fn add_inplace(&mut self, other: &Self) {
assert_eq!(
self.shape, other.shape,
"Tensors must have same shape for in-place addition"
);
for i in 0..self.data.len() {
self.data[i] += other.data[i];
}
}
fn sub_inplace(&mut self, other: &Self) {
assert_eq!(
self.shape, other.shape,
"Tensors must have same shape for in-place subtraction"
);
for i in 0..self.data.len() {
self.data[i] -= other.data[i];
}
}
fn mul_elementwise_inplace(&mut self, other: &Self) {
assert_eq!(
self.shape, other.shape,
"Tensors must have same shape for in-place multiplication"
);
for i in 0..self.data.len() {
self.data[i] *= other.data[i];
}
}
fn mul_scalar_inplace(&mut self, scalar: f64) {
for x in &mut self.data {
*x *= scalar;
}
}
fn div_scalar_inplace(&mut self, scalar: f64) {
assert!(scalar != 0.0, "Cannot divide by zero");
for x in &mut self.data {
*x /= scalar;
}
}
fn relu_inplace(&mut self) {
for x in &mut self.data {
if *x < 0.0 {
*x = 0.0;
}
}
}
fn leaky_relu_inplace(&mut self, alpha: f64) {
for x in &mut self.data {
if *x < 0.0 {
*x *= alpha;
}
}
}
fn sigmoid_inplace(&mut self) {
for x in &mut self.data {
*x = 1.0 / (1.0 + (-*x).exp());
}
}
fn tanh_inplace(&mut self) {
for x in &mut self.data {
*x = x.tanh();
}
}
fn clamp_inplace(&mut self, min: f64, max: f64) {
assert!(min <= max, "min must be <= max");
for x in &mut self.data {
*x = x.clamp(min, max);
}
}
fn zero_inplace(&mut self) {
for x in &mut self.data {
*x = 0.0;
}
}
fn fill_inplace(&mut self, value: f64) {
for x in &mut self.data {
*x = value;
}
}
}
pub trait VecInPlace {
fn add_inplace(&mut self, other: &[f64]);
fn mul_scalar_inplace(&mut self, scalar: f64);
fn relu_inplace(&mut self);
fn zero_inplace(&mut self);
}
impl VecInPlace for Vec<f64> {
fn add_inplace(&mut self, other: &[f64]) {
assert_eq!(self.len(), other.len());
for (x, &y) in self.iter_mut().zip(other.iter()) {
*x += y;
}
}
fn mul_scalar_inplace(&mut self, scalar: f64) {
for x in self.iter_mut() {
*x *= scalar;
}
}
fn relu_inplace(&mut self) {
for x in self.iter_mut() {
if *x < 0.0 {
*x = 0.0;
}
}
}
fn zero_inplace(&mut self) {
for x in self.iter_mut() {
*x = 0.0;
}
}
}
pub struct InPlaceBatch<'a, const N: usize> {
tensors: Vec<&'a mut Tensor<N>>,
}
impl<'a, const N: usize> InPlaceBatch<'a, N> {
pub fn new() -> Self {
Self {
tensors: Vec::new(),
}
}
pub fn add(&mut self, tensor: &'a mut Tensor<N>) {
self.tensors.push(tensor);
}
pub fn mul_scalar_all(&mut self, scalar: f64) {
for tensor in &mut self.tensors {
tensor.mul_scalar_inplace(scalar);
}
}
pub fn relu_all(&mut self) {
for tensor in &mut self.tensors {
tensor.relu_inplace();
}
}
pub fn zero_all(&mut self) {
for tensor in &mut self.tensors {
tensor.zero_inplace();
}
}
}
impl<'a, const N: usize> Default for InPlaceBatch<'a, N> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_inplace() {
let mut a = Tensor::<2>::filled([2, 2], 1.0);
let b = Tensor::<2>::filled([2, 2], 2.0);
a.add_inplace(&b);
assert_eq!(a.data, vec![3.0, 3.0, 3.0, 3.0]);
}
#[test]
fn test_sub_inplace() {
let mut a = Tensor::<2>::filled([2, 2], 5.0);
let b = Tensor::<2>::filled([2, 2], 2.0);
a.sub_inplace(&b);
assert_eq!(a.data, vec![3.0, 3.0, 3.0, 3.0]);
}
#[test]
fn test_mul_elementwise_inplace() {
let mut a = Tensor::<2>::filled([2, 2], 3.0);
let b = Tensor::<2>::filled([2, 2], 2.0);
a.mul_elementwise_inplace(&b);
assert_eq!(a.data, vec![6.0, 6.0, 6.0, 6.0]);
}
#[test]
fn test_mul_scalar_inplace() {
let mut tensor = Tensor::<2>::filled([2, 2], 2.0);
tensor.mul_scalar_inplace(3.0);
assert_eq!(tensor.data, vec![6.0, 6.0, 6.0, 6.0]);
}
#[test]
fn test_div_scalar_inplace() {
let mut tensor = Tensor::<2>::filled([2, 2], 10.0);
tensor.div_scalar_inplace(2.0);
assert_eq!(tensor.data, vec![5.0, 5.0, 5.0, 5.0]);
}
#[test]
fn test_relu_inplace() {
let mut tensor = Tensor::<2>::from_data([2, 2], vec![-1.0, 2.0, -3.0, 4.0]).unwrap();
tensor.relu_inplace();
assert_eq!(tensor.data, vec![0.0, 2.0, 0.0, 4.0]);
}
#[test]
fn test_leaky_relu_inplace() {
let mut tensor = Tensor::<2>::from_data([2, 2], vec![-2.0, 2.0, -4.0, 4.0]).unwrap();
tensor.leaky_relu_inplace(0.1);
assert_eq!(tensor.data, vec![-0.2, 2.0, -0.4, 4.0]);
}
#[test]
fn test_sigmoid_inplace() {
let mut tensor = Tensor::<2>::from_data([1, 2], vec![0.0, 1.0]).unwrap();
tensor.sigmoid_inplace();
assert!((tensor.data[0] - 0.5).abs() < 1e-6);
assert!((tensor.data[1] - 0.7310585786).abs() < 1e-6);
}
#[test]
fn test_tanh_inplace() {
let mut tensor = Tensor::<2>::from_data([1, 2], vec![0.0, 1.0]).unwrap();
tensor.tanh_inplace();
assert_eq!(tensor.data[0], 0.0);
assert!((tensor.data[1] - 0.7615941559).abs() < 1e-6);
}
#[test]
fn test_clamp_inplace() {
let mut tensor = Tensor::<2>::from_data([2, 2], vec![-5.0, 0.0, 5.0, 10.0]).unwrap();
tensor.clamp_inplace(-2.0, 7.0);
assert_eq!(tensor.data, vec![-2.0, 0.0, 5.0, 7.0]);
}
#[test]
fn test_zero_inplace() {
let mut tensor = Tensor::<2>::filled([2, 2], 42.0);
tensor.zero_inplace();
assert_eq!(tensor.data, vec![0.0, 0.0, 0.0, 0.0]);
}
#[test]
fn test_fill_inplace() {
let mut tensor = Tensor::<2>::zeros([2, 2]);
tensor.fill_inplace(7.5);
assert_eq!(tensor.data, vec![7.5, 7.5, 7.5, 7.5]);
}
#[test]
fn test_vec_inplace() {
let mut vec = vec![1.0, -2.0, 3.0, -4.0];
vec.mul_scalar_inplace(2.0);
assert_eq!(vec, vec![2.0, -4.0, 6.0, -8.0]);
vec.relu_inplace();
assert_eq!(vec, vec![2.0, 0.0, 6.0, 0.0]);
vec.add_inplace(&[1.0, 2.0, 3.0, 4.0]);
assert_eq!(vec, vec![3.0, 2.0, 9.0, 4.0]);
vec.zero_inplace();
assert_eq!(vec, vec![0.0, 0.0, 0.0, 0.0]);
}
#[test]
fn test_inplace_batch() {
let mut t1 = Tensor::<2>::filled([2, 2], 2.0);
let mut t2 = Tensor::<2>::filled([2, 2], 3.0);
let mut batch = InPlaceBatch::new();
batch.add(&mut t1);
batch.add(&mut t2);
batch.mul_scalar_all(0.5);
assert_eq!(t1.data, vec![1.0, 1.0, 1.0, 1.0]);
assert_eq!(t2.data, vec![1.5, 1.5, 1.5, 1.5]);
}
#[test]
fn test_chained_inplace_ops() {
let mut tensor = Tensor::<2>::filled([2, 2], 10.0);
tensor.mul_scalar_inplace(2.0); tensor.add_inplace(&Tensor::<2>::filled([2, 2], -5.0)); tensor.div_scalar_inplace(3.0); tensor.clamp_inplace(0.0, 4.0);
assert_eq!(tensor.data, vec![4.0, 4.0, 4.0, 4.0]);
}
}