ANT a NdArry

Photo by rawpixel on Unsplash

ant

ANT is a NdArray that is developing with c++ and Cuda from scratch. Currently, it supports some basic array operations just like NumPy or Cupy but in language, C++ instate of python. It is an educational project to understand how a NumPy-like library works, and how automatic broadcasting work. Apart from this project also helps to learn Cuda programming and how our GPU work to do parallel programming. To learn how this ant array work visits the documentation and test folder.

API Documentations: here

Some functionalities of ant NdArray

Array Creation

#include "array.h"

ndarray::Shape s = {3};
double a[] = {1,2,3};
ndarray::Array A(s, a);

EXPECT_EQ(A.rank(),1);
EXPECT_EQ(A.size(), 3);

Addition

ndarray::Shape s = {3};
double a[] = {1,2,3};

ndarray::Array A(s, a);
ndarray::Array B(s, a);

auto C = A + B;


EXPECT_EQ(A.rank(),1);
EXPECT_EQ(B.rank(), 1);
EXPECT_EQ(C.rank(), 1);
EXPECT_EQ(C.size(), 3);

double * cActualData = C.hostData();
double cExpectedData[] = {2,4,6};
for(int i =0; i< C.size(); i++){
    EXPECT_EQ(cActualData[i], cExpectedData[i]);
}

Broadcasting

ndarray::Shape a_shape = {4,1};
ndarray::Shape b_shape = {1,3};

double a_data[] ={1,2,3,4};
double b_data[] = {1,2,3};
ndarray::Array A(a_shape, a_data);
ndarray::Array B(b_shape, b_data);

auto C = A+B;

double *actual = C.hostData();
double expected[] = {2,3,4,3,4,5,4,5,6,5,6,7};

VectorEQ(C.shape(), {4,3});
DoubleArrayEQ(actual, expected, 12); 

Matmul

 ndarray::Shape a_shape = {2,4};
ndarray::Shape b_shape = {4,2};

double a_data[] = {1,2,3,4,5,6,7,8};
double b_data[] ={8,7,6,5,4,3,2,1};
ndarray::Array A(a_shape, a_data);
ndarray::Array B(b_shape, b_data);

auto C = A.matmul(B);

double *actual = C.hostData();
double expected[] = {40,30,120,94};
VectorEQ(C.shape(), {2,2});
DoubleArrayEQ(actual, expected, 4); 

Matmul with broadcasting

 ndarray::Shape a_shape = {2,2};
ndarray::Shape b_shape = {2,2,3};

double a_data[] = {1,2,3,4};
double b_data[] ={12,11,10,9,8,7,6,5,4,3,2,1};
ndarray::Array A(a_shape, a_data);
ndarray::Array B(b_shape, b_data);

auto C = A.matmul(B);

double *actual = C.hostData();
double expected[] = {30,27,24,72,65,58, 12,9,6,30,23,16};

VectorEQ(C.shape(), {2,2,3});
DoubleArrayEQ(actual, expected, 12); 

Transpose

ndarray::Shape a_shape = {5,4};
double a_data[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};

ndarray::Array A(a_shape, a_data);
A.transpose({1,0});

double expected[][5] = {{1,5,9,13,17},{2,6,10,14,18},{3,7,11,15,19},{4,8,12,16,20}};
VectorEQ(A.shape(), {4,5});
VectorEQ(A.stride(), {1,4});

Indexing

ndarray::Shape shape= {2,3};
double data[][3] = {{1,2,3},{10,20,30}};
ndarray::Array A(shape, *data);

EXPECT_EQ(A(0,0), 1);
EXPECT_EQ(A(0,1), 2);
EXPECT_EQ(A(0,2), 3);

EXPECT_EQ(A(1,0), 10);
EXPECT_EQ(A(1,1), 20);
EXPECT_EQ(A(1,2), 30);

Slicing

ndarray::Shape a_shape = {3,3,2};
double a_data[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18};

ndarray::Array A(a_shape, a_data);
ndarray::Array B = A[{{1,5,1}, {0,-1},{1}}];

double *actual = B.hostData();
double expected[] = {8,10,14,16};
VectorEQ(B.shape(), {2,2});
DoubleArrayEQ(actual, expected, 4); 

Details example are found in the test folder, where different kind of scenerio was tasted.