briny_ai 0.1.0

Minimal autodiff + tensor engine with serialization
Documentation
use briny_ai::{tensors::{Tensor, WithGrad}, tensor};
use briny_ai::backprop::{matmul, relu, mse_loss, sgd};

#[test]
fn test_tensor_creation() {
    let t = Tensor::new(vec![2, 2], vec![1.0, 2.0, 3.0, 4.0]);
    assert_eq!(t.shape, vec![2, 2]);
    assert_eq!(t.data, vec![1.0, 2.0, 3.0, 4.0]);
}

#[test]
fn test_tensor_macro() {
    let t = tensor!([[1.0, 2.0], [3.0, 4.0]]);
    assert_eq!(t.shape, vec![2, 2]);
    assert_eq!(t.data, vec![1.0, 2.0, 3.0, 4.0]);
}

#[test]
fn test_matmul_backprop() {
    let a = WithGrad {
        value: Tensor::new(vec![2, 3], vec![
            1.0, 2.0, 3.0,
            4.0, 5.0, 6.0
        ]),
        grad: Tensor::new(vec![2, 3], vec![0.0; 6])
    };
    let b = WithGrad {
        value: Tensor::new(vec![3, 2], vec![
            7.0, 8.0,
            9.0, 10.0,
            11.0, 12.0
        ]),
        grad: Tensor::new(vec![3, 2], vec![0.0; 6])
    };
    
    let (output, backward) = matmul(&a, &b);
    assert_eq!(output.shape, vec![2, 2]);
    let grad_output = Tensor::new(vec![2, 2], vec![1.0, 1.0, 1.0, 1.0]);
    let (grad_a, grad_b) = backward(&grad_output);
    assert_eq!(grad_a.shape, vec![2, 3]);
    assert_eq!(grad_b.shape, vec![3, 2]);
}

#[test]
fn test_relu_backprop() {
    let t = WithGrad {
        value: Tensor::new(vec![3], vec![-1.0, 0.0, 1.0]),
        grad: Tensor::new(vec![3], vec![0.0; 3]),
    };
    let (output, backward) = relu(&t);
    assert_eq!(output.data, vec![0.0, 0.0, 1.0]);
    let grad_output = Tensor::new(vec![3], vec![1.0, 1.0, 1.0]);
    let grad = backward(&grad_output);
    assert_eq!(grad.data, vec![0.0, 0.0, 1.0]);
}

#[test]
fn test_mse_loss() {
    let pred = WithGrad {
        value: Tensor::new(vec![2], vec![1.0, 2.0]),
        grad: Tensor::new(vec![2], vec![0.0; 2]),
    };
    let target = Tensor::new(vec![2], vec![1.5, 2.5]);
    let (loss, backward) = mse_loss(&pred, &target);
    let grad = backward(1.0);
    assert_eq!(loss, 0.25);
    assert_eq!(grad.data, vec![-0.5, -0.5]);
}

#[test]
fn test_sgd() {
    let mut w = WithGrad {
        value: Tensor::new(vec![2], vec![1.0, 2.0]),
        grad: Tensor::new(vec![2], vec![0.1, 0.2]),
    };
    sgd(&mut w, 0.5);
    assert_eq!(w.value.data, vec![0.95, 1.9]);
    assert_eq!(w.grad.data, vec![0.0, 0.0]);
}