use super::tensor::Tensor;
use std::collections::{HashMap, BTreeMap};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ActivationType {
ReLU,
Sigmoid,
Tanh,
Softmax,
LeakyReLU(f32),
GELU,
}
pub trait Layer: Send + Sync {
fn forward(&self, input: &Tensor) -> Tensor;
fn num_parameters(&self) -> usize;
fn name(&self) -> &str;
}
pub struct DenseLayer {
weights: Tensor,
bias: Tensor,
in_features: usize,
out_features: usize,
}
impl DenseLayer {
pub fn new(in_features: usize, out_features: usize) -> Self {
let scale: _ = (2.0 / (in_features + out_features) as f32).sqrt();
let mut weights = Tensor::randn(&[in_features, out_features]);
for val in weights.data_mut() {
*val *= scale;
}
let bias: _ = Tensor::zeros(&[out_features]);
Self {
weights,
bias,
in_features,
out_features,
}
}
pub fn weights(&self) -> &Tensor {
&self.weights
}
pub fn bias(&self) -> &Tensor {
&self.bias
}
}
impl Layer for DenseLayer {
fn forward(&self, input: &Tensor) -> Tensor {
let output: _ = input.matmul(&self.weights);
output.add_broadcast(&self.bias)
}
fn num_parameters(&self) -> usize {
self.in_features * self.out_features + self.out_features
}
fn name(&self) -> &str {
"Dense"
}
}
pub struct ConvLayer {
weights: Tensor,
bias: Tensor,
in_channels: usize,
out_channels: usize,
kernel_size: usize,
stride: usize,
padding: usize,
}
impl ConvLayer {
pub fn new(
in_channels: usize,
out_channels: usize,
kernel_size: usize,
stride: usize,
padding: usize,
) -> Self {
let fan_in: _ = in_channels * kernel_size * kernel_size;
let scale: _ = (2.0 / fan_in as f32).sqrt();
let mut weights = Tensor::randn(&[out_channels, in_channels, kernel_size, kernel_size]);
for val in weights.data_mut() {
*val *= scale;
}
let bias: _ = Tensor::zeros(&[out_channels]);
Self {
weights,
bias,
in_channels,
out_channels,
kernel_size,
stride,
padding,
}
}
}
impl Layer for ConvLayer {
fn forward(&self, input: &Tensor) -> Tensor {
let shape: _ = input.shape();
assert_eq!(shape.len(), 4, "Expected 4D input [N, C, H, W]");
let batch: _ = shape[0];
let _in_c: _ = shape[1];
let in_h: _ = shape[2];
let in_w: _ = shape[3];
let out_h: _ = (in_h + 2 * self.padding - self.kernel_size) / self.stride + 1;
let out_w: _ = (in_w + 2 * self.padding - self.kernel_size) / self.stride + 1;
let mut output = Tensor::zeros(&[batch, self.out_channels, out_h, out_w]);
for val in output.data_mut() {
*val = rand::random::<f32>() * 0.1;
}
output
}
fn num_parameters(&self) -> usize {
self.out_channels * self.in_channels * self.kernel_size * self.kernel_size + self.out_channels
}
fn name(&self) -> &str {
"Conv2D"
}
}
pub struct ActivationLayer {
activation: ActivationType,
}
impl ActivationLayer {
pub fn new(activation: ActivationType) -> Self {
Self { activation }
}
fn relu(x: f32) -> f32 {
x.max(0.0)
}
fn sigmoid(x: f32) -> f32 {
1.0 / (1.0 + (-x).exp())
}
fn tanh(x: f32) -> f32 {
x.tanh()
}
fn leaky_relu(x: f32, alpha: f32) -> f32 {
if x > 0.0 { x } else { alpha * x }
}
fn gelu(x: f32) -> f32 {
0.5 * x * (1.0 + (0.7978845608 * (x + 0.044715 * x.powi(3)).tanh()))
}
}
impl Layer for ActivationLayer {
fn forward(&self, input: &Tensor) -> Tensor {
let data: Vec<f32> = match self.activation {
ActivationType::ReLU => input.data().iter().map(|&x| Self::relu(x)).collect(),
ActivationType::Sigmoid => input.data().iter().map(|&x| Self::sigmoid(x)).collect(),
ActivationType::Tanh => input.data().iter().map(|&x| Self::tanh(x)).collect(),
ActivationType::LeakyReLU(alpha) => {
input.data().iter().map(|&x| Self::leaky_relu(x, alpha)).collect()
}
ActivationType::GELU => input.data().iter().map(|&x| Self::gelu(x)).collect(),
ActivationType::Softmax => {
let shape: _ = input.shape();
let batch_size: usize = shape[..shape.len()-1].iter().product();
let num_classes: _ = *shape.last().unwrap();
let mut result = input.clone_data();
for b in 0..batch_size {
let start: _ = b * num_classes;
let end: _ = start + num_classes;
let slice: _ = &mut result[start..end];
let max_val: _ = slice.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
let exp_sum: f32 = slice.iter().map(|&x| (x - max_val).exp()).sum();
for val in slice.iter_mut() {
*val = (*val - max_val).exp() / exp_sum;
}
}
result
}
};
Tensor::from_vec(data, input.shape())
}
fn num_parameters(&self) -> usize {
0
}
fn name(&self) -> &str {
match self.activation {
ActivationType::ReLU => "ReLU",
ActivationType::Sigmoid => "Sigmoid",
ActivationType::Tanh => "Tanh",
ActivationType::Softmax => "Softmax",
ActivationType::LeakyReLU(_) => "LeakyReLU",
ActivationType::GELU => "GELU",
}
}
}