diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9fd45e0..f6b3874 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,4 +1,4 @@ -name: Rust +name: Rust CI on: push: @@ -15,8 +15,38 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose + # Checkout the repository + - uses: actions/checkout@v4 + + # Install Rust + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + # Cache dependencies for faster builds + - name: Cache Cargo + uses: actions/cache@v3 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + # Build the project + - name: Build + run: cargo build --verbose + + # Run tests + - name: Run tests + run: cargo test --verbose + + # Run linter (clippy) + - name: Run Clippy + run: cargo clippy --all-targets --all-features -- -D warnings + + # Run formatter check + - name: Run rustfmt + run: cargo fmt -- --check + diff --git a/src/lib.rs b/src/lib.rs index bf00564..5eff40f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,8 +6,7 @@ pub mod transformer { } pub mod math { + pub mod activation; pub mod linear_algebra; pub mod softmax; - pub mod activation; - -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 029c7d1..b776082 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,3 @@ -use ndarray::{array, Array1}; -use Transformer::math::linear_algebra::dotproduct; - fn main() { - println!("runs successfully!"); } diff --git a/src/math/activation.rs b/src/math/activation.rs index 5c5f383..7cc9dd5 100644 --- a/src/math/activation.rs +++ b/src/math/activation.rs @@ -1,33 +1,21 @@ -use ndarray::{Array2}; - - +use ndarray::Array2; pub fn relu(a: &Array2) -> Array2 { - - a.mapv(|x| if x > 0.0 { x } else { 0.0 }) // mapValue returns a new and owned Array!! + a.mapv(|x| if x > 0.0 { x } else { 0.0 }) // mapValue returns a new and owned Array!! // Relu : B(i,j) = max(0,A(i,j)) } - - - use std::f32::consts::PI; // GELU (Gaussian Error Linear Unit) //Formula: // GELU(x)=x⋅Φ(x) // with GELU(x)≈0.5x(1+tanh(sqrt 2/π)(x+0.044715x^3))) // Empirically better than Relu -pub fn gelu(a: &Array2) -> Array2 { +pub fn gelu(a: &Array2) -> Array2 { fn gelu_calc(y: f32) -> f32 { - y * 0.5f32 * (1f32 + ((2f32/PI).sqrt()*(y + 0.044715f32*y.powi(3))).tanh()) + y * 0.5f32 * (1f32 + ((2f32 / PI).sqrt() * (y + 0.044715f32 * y.powi(3))).tanh()) } - a.mapv(|x| gelu_calc(x) ) + a.mapv(gelu_calc) } - - - - - - diff --git a/src/math/linear_algebra.rs b/src/math/linear_algebra.rs index cac34b4..8cf4595 100644 --- a/src/math/linear_algebra.rs +++ b/src/math/linear_algebra.rs @@ -1,7 +1,5 @@ - -use ndarray::{Array1, Array2}; use ndarray::linalg::general_mat_mul; - +use ndarray::{Array1, Array2}; /// Performs matrix multiplication between two 2D arrays. /// @@ -11,7 +9,6 @@ use ndarray::linalg::general_mat_mul; /// /// # Returns /// An `Array2` representing the result of the matrix multiplication. - pub fn matmul(a: &Array2, b: &Array2) -> Result, &'static str> { if a.ncols() != b.nrows() { return Err("Matrix dimensions are incompatible for multiplication."); @@ -21,9 +18,6 @@ pub fn matmul(a: &Array2, b: &Array2) -> Result, &'static Ok(result) } - - pub fn dotproduct(a: &Array1, b: &Array1) -> f32 { a.dot(b) } - diff --git a/src/math/softmax.rs b/src/math/softmax.rs index aabaa2a..932a0b0 100644 --- a/src/math/softmax.rs +++ b/src/math/softmax.rs @@ -1,9 +1,6 @@ #![allow(unused_imports)] // {array} import is not recognized as it is used in #[test] use ndarray::{array, Array, Array1, Array2, ArrayView1, Axis}; - - - pub fn softmax_vector(vec: ArrayView1) -> Array1 { let max = vec.fold(f32::NEG_INFINITY, |a, &b| a.max(b)); // Stabilize by subtracting max let exp_vec = vec.mapv(|x| (x - max).exp()); @@ -11,15 +8,10 @@ pub fn softmax_vector(vec: ArrayView1) -> Array1 { exp_vec / sum } - pub fn softmax_matrix(mat: &Array2) -> Array2 { - convert_to_array2(mat.map_axis(Axis(1), |row| softmax_vector(row))) + convert_to_array2(mat.map_axis(Axis(1), softmax_vector)) } - - - - fn convert_to_array2(array1d: Array, ndarray::Ix1>) -> Array2 { // Check if the input array is non-empty assert!(!array1d.is_empty(), "Input array must not be empty."); @@ -30,7 +22,11 @@ fn convert_to_array2(array1d: Array, ndarray::Ix1>) -> Array2 { let mut data = Vec::new(); for row in array1d.iter() { - assert_eq!(row.len(), cols, "All rows must have the same number of columns."); + assert_eq!( + row.len(), + cols, + "All rows must have the same number of columns." + ); data.extend_from_slice(row.as_slice().unwrap()); // Flatten each row and push into Vec } @@ -38,9 +34,7 @@ fn convert_to_array2(array1d: Array, ndarray::Ix1>) -> Array2 { } #[test] -pub fn convert_to_array2_test(){ - - +pub fn convert_to_array2_test() { let data = Array::from(vec![ Array1::from(vec![1.0, 2.0, 3.0]), Array1::from(vec![4.0, 5.0, 6.0]), @@ -50,10 +44,9 @@ pub fn convert_to_array2_test(){ // Convert to Array2 let matrix = convert_to_array2(data); - let d2array :Array2 = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]; + let d2array: Array2 = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]; assert_eq!(matrix.nrows(), 3); assert_eq!(matrix.ncols(), 3); - assert_eq!(matrix,d2array); - + assert_eq!(matrix, d2array); } diff --git a/tests/activation_tests.rs b/tests/activation_tests.rs index 281f493..1d617ff 100644 --- a/tests/activation_tests.rs +++ b/tests/activation_tests.rs @@ -2,33 +2,32 @@ use ndarray::array; use Transformer::math::activation::{gelu, relu}; #[test] -pub fn test_relu(){ +pub fn test_relu() { let input = array![[1.0, -1.0], [0.5, -0.5]]; let result = relu(&input); assert_eq!(result, array![[1.0, 0.0], [0.5, 0.0]]); - } #[test] -pub fn one_test_gelu(){ - - let binding =gelu(&array![[1.0]]) ; - let result = binding.get((0,0)).unwrap(); +pub fn one_test_gelu() { + let binding = gelu(&array![[1.0]]); + let result = binding.get((0, 0)).unwrap(); // Define an acceptable tolerance (epsilon) let epsilon = 1e-6; // allow up to 1e-6 difference // Assert that the result is within the tolerance of the expected value - assert!((result - 0.841192f32).abs() < epsilon, "Test failed: result was {}", result); + assert!( + (result - 0.841192f32).abs() < epsilon, + "Test failed: result was {}", + result + ); } #[test] fn test_gelu_2x2() { // Create a 2x2 matrix - let input = array![ - [1.0, 2.0], - [3.0, 4.0] - ]; + let input = array![[1.0, 2.0], [3.0, 4.0]]; // Apply GELU element-wise let result = gelu(&input); @@ -37,15 +36,17 @@ fn test_gelu_2x2() { let epsilon = 1e-6; // Expected results for GELU(1.0), GELU(2.0), GELU(3.0), GELU(4.0) - let expected = array![ - [0.841192, 1.9545977], - [2.9963627, 3.99993] - ]; + let expected = array![[0.841192, 1.9545977], [2.9963627, 3.99993]]; // Assert that each element in the result is within tolerance of the expected value for (r, expected_row) in result.outer_iter().zip(expected.outer_iter()) { for (res, exp) in r.iter().zip(expected_row.iter()) { - assert!((res - exp).abs() < epsilon, "Test failed: result was {}, expected {}", res, exp); + assert!( + (res - exp).abs() < epsilon, + "Test failed: result was {}, expected {}", + res, + exp + ); } } } diff --git a/tests/linear_algebra_test.rs b/tests/linear_algebra_test.rs index ee36d9c..1133044 100644 --- a/tests/linear_algebra_test.rs +++ b/tests/linear_algebra_test.rs @@ -1,25 +1,14 @@ -use Transformer::math::linear_algebra::{dotproduct, matmul}; -use ndarray::{array, Array1}; // Assuming you're using ndarray for matrices - +use ndarray::{array, Array1}; +use Transformer::math::linear_algebra::{dotproduct, matmul}; // Assuming you're using ndarray for matrices #[test] fn test_matmul_valid_input() { // Arrange: Define input matrices - let a = array![ - [1.0, 2.0, 3.0], - [4.0, 5.0, 6.0] - ]; // 2x3 matrix - let b = array![ - [7.0, 8.0], - [9.0, 10.0], - [11.0, 12.0] - ]; // 3x2 matrix + let a = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]; // 2x3 matrix + let b = array![[7.0, 8.0], [9.0, 10.0], [11.0, 12.0]]; // 3x2 matrix // Expected result after matrix multiplication - let expected = array![ - [58.0, 64.0], - [139.0, 154.0] - ]; // 2x2 matrix + let expected = array![[58.0, 64.0], [139.0, 154.0]]; // 2x2 matrix // Act: Perform the multiplication let result = matmul(&a, &b); // Assuming matmul returns Result @@ -34,25 +23,23 @@ fn test_matmul_valid_input() { #[test] fn test_matmul_invalid_input() { // Arrange: Define input matrices with mismatched dimensions - let a = array![ - [1.0, 2.0], - [3.0, 4.0] - ]; // 2x2 matrix - let b = array![ - [5.0, 6.0] - ]; // 1x2 matrix (mismatched dimensions) + let a = array![[1.0, 2.0], [3.0, 4.0]]; // 2x2 matrix + let b = array![[5.0, 6.0]]; // 1x2 matrix (mismatched dimensions) // Act: Perform the multiplication, expecting an error let result = matmul(&a, &b); // Assert: Ensure the result is an error due to incompatible dimensions - assert_eq!(result, Err("Matrix dimensions are incompatible for multiplication.")); + assert_eq!( + result, + Err("Matrix dimensions are incompatible for multiplication.") + ); } #[test] -fn test_dotproduct(){ - let a : Array1 = array![ 1.0, 2.0, 3.0 ]; - let b : Array1 = array![ 4.0, 5.0, 5.0 ]; +fn test_dotproduct() { + let a: Array1 = array![1.0, 2.0, 3.0]; + let b: Array1 = array![4.0, 5.0, 5.0]; let expected = (4 + 10 + 15) as f32; let result = dotproduct(&a, &b); @@ -60,18 +47,19 @@ fn test_dotproduct(){ assert_eq!(result, expected); } #[test] -fn test_floats_dotproduct(){ - let a : Array1 = array![ 2.9, 7.68, 2.333 ]; - let b : Array1 = array![ 0.74, 1.2, 5.111 ]; +fn test_floats_dotproduct() { + let a: Array1 = array![2.9, 7.68, 2.333]; + let b: Array1 = array![0.74, 1.2, 5.111]; - let expected = (2.9*0.74 + 7.68*1.2 + 2.333*5.111) as f32; + let expected = (2.9 * 0.74 + 7.68 * 1.2 + 2.333 * 5.111) as f32; let result = dotproduct(&a, &b); assert_eq!(result, expected); -}#[test] -fn test_empty_dotproduct(){ - let a : Array1 = array![ ]; - let b : Array1 = array![ ]; +} +#[test] +fn test_empty_dotproduct() { + let a: Array1 = array![]; + let b: Array1 = array![]; let expected = 0.0; let result = dotproduct(&a, &b); diff --git a/tests/softmax_test.rs b/tests/softmax_test.rs index 65d6357..3d700ad 100644 --- a/tests/softmax_test.rs +++ b/tests/softmax_test.rs @@ -1,36 +1,39 @@ use ndarray::{array, Array, Array1, Array2, Ix1}; use Transformer::math::softmax::{softmax_matrix, softmax_vector}; - - #[test] -pub fn test_softmax_vector(){ - let vec: Array = Array1::from(vec![-1.0,0.0,3.0,5.0]); +pub fn test_softmax_vector() { + let vec: Array = Array1::from(vec![-1.0, 0.0, 3.0, 5.0]); - let res = softmax_vector(vec.view()); + let res = softmax_vector(vec.view()); - let expected : Array1 = Array1::from(vec![0.0021656966, 0.0058869733, 0.11824302, 0.87370431]); + let expected: Array1 = + Array1::from(vec![0.0021656966, 0.0058869733, 0.11824302, 0.87370431]); - assert_eq!(res, expected); + assert_eq!(res, expected); } #[test] -pub fn test_softmax_matrix(){ - let mat1 : Array2 = array![[1.0,2.0,3.0], [4.0,5.0,6.0],[7.0,8.0,9.0]]; - let expected1 : Array2 = array![[0.0900305732,0.2447284711,0.6652409558],[ 0.0900305732,0.2447284711,0.6652409558],[0.0900305732,0.2447284711,0.6652409558]]; +pub fn test_softmax_matrix() { + let mat1: Array2 = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]; + let expected1: Array2 = array![ + [0.0900305732, 0.2447284711, 0.6652409558], + [0.0900305732, 0.2447284711, 0.6652409558], + [0.0900305732, 0.2447284711, 0.6652409558] + ]; let res1: Array2 = softmax_matrix(&mat1); assert_eq!(res1, expected1); - - let mat2 : Array2 = array![[-1.0,3.0,7.0], [0.0,2.5,4.0],[7.0,8.0,9.0]]; - let expected2: Array2 = array![[0.00032932044,0.0179802867,0.98169035],[ 0.0147534745,0.17973413,0.805512412],[0.0900305732,0.2447284711,0.6652409558]]; + let mat2: Array2 = array![[-1.0, 3.0, 7.0], [0.0, 2.5, 4.0], [7.0, 8.0, 9.0]]; + let expected2: Array2 = array![ + [0.00032932044, 0.0179802867, 0.98169035], + [0.0147534745, 0.17973413, 0.805512412], + [0.0900305732, 0.2447284711, 0.6652409558] + ]; let res2: Array2 = softmax_matrix(&mat2); assert_eq!(res2, expected2); - - - -} \ No newline at end of file +}