Skip to content

Commit

Permalink
more sophisticated rust.yml with cargo clippy and fmt, therefore refa…
Browse files Browse the repository at this point in the history
…ctored code to comply to all good practices
  • Loading branch information
JakubSchwenkbeck committed Dec 4, 2024
1 parent 62a04af commit 8214d17
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 121 deletions.
42 changes: 36 additions & 6 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Rust
name: Rust CI

on:
push:
Expand All @@ -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

5 changes: 2 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ pub mod transformer {
}

pub mod math {
pub mod activation;
pub mod linear_algebra;
pub mod softmax;
pub mod activation;

}
}
4 changes: 0 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
use ndarray::{array, Array1};
use Transformer::math::linear_algebra::dotproduct;

fn main() {

println!("runs successfully!");
}
22 changes: 5 additions & 17 deletions src/math/activation.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,21 @@
use ndarray::{Array2};


use ndarray::Array2;

pub fn relu(a: &Array2<f32>) -> Array2<f32> {

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<f32>) -> Array2<f32> {
pub fn gelu(a: &Array2<f32>) -> Array2<f32> {
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)
}






8 changes: 1 addition & 7 deletions src/math/linear_algebra.rs
Original file line number Diff line number Diff line change
@@ -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.
///
Expand All @@ -11,7 +9,6 @@ use ndarray::linalg::general_mat_mul;
///
/// # Returns
/// An `Array2<f32>` representing the result of the matrix multiplication.
pub fn matmul(a: &Array2<f32>, b: &Array2<f32>) -> Result<Array2<f32>, &'static str> {
if a.ncols() != b.nrows() {
return Err("Matrix dimensions are incompatible for multiplication.");
Expand All @@ -21,9 +18,6 @@ pub fn matmul(a: &Array2<f32>, b: &Array2<f32>) -> Result<Array2<f32>, &'static
Ok(result)
}



pub fn dotproduct(a: &Array1<f32>, b: &Array1<f32>) -> f32 {
a.dot(b)
}

25 changes: 9 additions & 16 deletions src/math/softmax.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
#![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<f32>) -> Array1<f32> {
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());
let sum: f32 = exp_vec.sum();
exp_vec / sum
}


pub fn softmax_matrix(mat: &Array2<f32>) -> Array2<f32> {
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<Array1<f32>, ndarray::Ix1>) -> Array2<f32> {
// Check if the input array is non-empty
assert!(!array1d.is_empty(), "Input array must not be empty.");
Expand All @@ -30,17 +22,19 @@ fn convert_to_array2(array1d: Array<Array1<f32>, ndarray::Ix1>) -> Array2<f32> {
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
}

Array2::from_shape_vec((rows, cols), data).unwrap() // Convert Vec to 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]),
Expand All @@ -50,10 +44,9 @@ pub fn convert_to_array2_test(){
// Convert to Array2<f32>
let matrix = convert_to_array2(data);

let d2array :Array2<f32> = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]];
let d2array: Array2<f32> = 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);
}
33 changes: 17 additions & 16 deletions tests/activation_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
);
}
}
}
58 changes: 23 additions & 35 deletions tests/linear_algebra_test.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -34,44 +23,43 @@ 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<f32> = array![ 1.0, 2.0, 3.0 ];
let b : Array1<f32> = array![ 4.0, 5.0, 5.0 ];
fn test_dotproduct() {
let a: Array1<f32> = array![1.0, 2.0, 3.0];
let b: Array1<f32> = array![4.0, 5.0, 5.0];

let expected = (4 + 10 + 15) as f32;
let result = dotproduct(&a, &b);

assert_eq!(result, expected);
}
#[test]
fn test_floats_dotproduct(){
let a : Array1<f32> = array![ 2.9, 7.68, 2.333 ];
let b : Array1<f32> = array![ 0.74, 1.2, 5.111 ];
fn test_floats_dotproduct() {
let a: Array1<f32> = array![2.9, 7.68, 2.333];
let b: Array1<f32> = 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<f32> = array![ ];
let b : Array1<f32> = array![ ];
}
#[test]
fn test_empty_dotproduct() {
let a: Array1<f32> = array![];
let b: Array1<f32> = array![];

let expected = 0.0;
let result = dotproduct(&a, &b);
Expand Down
Loading

0 comments on commit 8214d17

Please sign in to comment.