📄 neural_network_logic_animated.rs
/home/palash/git/build-your-own-nn/src/examples/neural_network_logic_animated.rs
Language: rs • Lines: 152
use crate::{
    Layer, Rng,
    activation::{Activation, ActivationType},
    image_utils::{PlotColor, Trace, render_plot},
    linear::Linear,
    loss::bce_sigmoid_delta,
    tensor::{Tensor, TensorError},
};
use std::thread;
use std::time::Duration;

#[derive(Debug)]
pub enum AnimatedGate {
    AND,
    NAND,
    OR,
    NOR,
}

pub fn demonstrate_logic(rng: &mut dyn Rng, gate: AnimatedGate) -> Result<(), TensorError> {
    let mut linear_layer = Linear::new(3, 1, rng);

    let weight_init = match gate {
        AnimatedGate::AND => vec![10.0, -100.0, -15.0],
        AnimatedGate::NAND => vec![-8.0, -8.0, 5.0],
        AnimatedGate::OR => vec![35.0, 50.0, -10.0],
        AnimatedGate::NOR => vec![-15.0, -15.0, 10.0],
    };

    linear_layer.set_weight(Tensor::new(weight_init, vec![3, 1])?);
    let mut activation_layer = Activation::new(ActivationType::Sigmoid);

    let input = Tensor::new(
        vec![
            5.0, 5.0, 1.0, 5.0, 15.0, 1.0, 15.0, 5.0, 1.0, 15.0, 15.0, 1.0,
        ],
        vec![4, 3],
    )?;
    let actual = match gate {
        AnimatedGate::AND => Tensor::new(vec![0.0, 0.0, 0.0, 1.0], vec![4, 1])?,
        AnimatedGate::NAND => Tensor::new(vec![0.0, 1.0, 1.0, 1.0], vec![4, 1])?,
        AnimatedGate::OR => Tensor::new(vec![0.0, 1.0, 1.0, 1.0], vec![4, 1])?,
        AnimatedGate::NOR => Tensor::new(vec![0.0, 0.0, 0.0, 1.0], vec![4, 1])?,
    };

    let learning_rate = 0.015;
    let bounds = Some((0.0, 20.0, 0.0, 20.0));

    for epoch in 0..8000 {
        let linear_output = linear_layer.forward(&input)?;
        let activation_output = activation_layer.forward(&linear_output)?;

        if epoch % 15 == 0 {
            let mut traces = Vec::new();
            let w = linear_layer.weight().data();
            let w1 = w[0];
            let w2 = w[1];
            let b = w[2];

            let mut cyan_x = Vec::new();
            let mut cyan_y = Vec::new();
            let mut magenta_x = Vec::new();
            let mut magenta_y = Vec::new();

            for gx in (0..=20).step_by(2) {
                for gy in (0..=20).step_by(2) {
                    let x = gx as f32;
                    let y = gy as f32;

                    let decision = w1 * x + w2 * y + b;
                    if decision > 0.0 {
                        cyan_x.push(x);
                        cyan_y.push(y);
                    } else {
                        magenta_x.push(x);
                        magenta_y.push(y);
                    }
                }
            }

            traces.push(Trace {
                name: "Predict 1".to_string(),
                x: cyan_x,
                y: cyan_y,
                color: PlotColor::Cyan,
                is_line: false,
                hide_axes: false
            });

            traces.push(Trace {
                name: "Predict 0".to_string(),
                x: magenta_x,
                y: magenta_y,
                color: PlotColor::Magenta,
                is_line: false,
                hide_axes: false
            });

            let mut x_line = Vec::new();
            let mut y_line = Vec::new();
            for i in 0..=20 {
                let x = i as f32;
                let y = -(w1 * x + b) / w2;
                if y >= -2.0 && y <= 22.0 {
                    x_line.push(x);
                    y_line.push(y);
                }
            }
            traces.push(Trace {
                name: format!("Boundary (Epoch {})", epoch),
                x: x_line,
                y: y_line,
                color: PlotColor::Yellow,
                is_line: true,
                hide_axes: false
            });

            let x_coords = [5.0, 5.0, 15.0, 15.0];
            let y_coords = [5.0, 15.0, 5.0, 15.0];
            let targets = actual.data();

            for i in 0..4 {
                let goal_color = if targets[i] > 0.5 {
                    PlotColor::Green
                } else {
                    PlotColor::Red
                };
                traces.push(Trace {
                    name: format!("P{}", i),
                    x: vec![x_coords[i]],
                    y: vec![y_coords[i]],
                    color: goal_color,
                    is_line: false,
                    hide_axes: false
                });
            }

            render_plot(
                &traces,
                70,
                25,
                bounds,
                format!("Binary Classification({:?} Gate)", gate),
            );
            thread::sleep(Duration::from_millis(10));
        }

        let delta = bce_sigmoid_delta(&activation_output, &actual)?;
        let _ = linear_layer.backward(&delta, learning_rate)?;
    }
    Ok(())
}