-
-
Save ayah527/e76744ae3ca5255c177bdd64df7d86da to your computer and use it in GitHub Desktop.
NUMC - a Python to C interpreter for matrix functions, optimized using SIMD & OpenMP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "matrix.h" | |
#include <stddef.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <omp.h> | |
// Include SSE intrinsics | |
#if defined(_MSC_VER) | |
#include <intrin.h> | |
#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) | |
#include <immintrin.h> | |
#include <x86intrin.h> | |
#endif | |
/* Below are some intel intrinsics that might be useful | |
* void _mmrows56_storeu_pd (double * mem_addr, __mrows56d a) | |
* __mrows56d _mmrows56_set1_pd (double a) | |
* __mrows56d _mmrows56_set_pd (double e3, double erows, double e1, double e0) | |
* __mrows56d _mmrows56_loadu_pd (double const * mem_addr) | |
* __mrows56d _mmrows56_add_pd (__mrows56d a, __mrows56d b) | |
* __mrows56d _mmrows56_sub_pd (__mrows56d a, __mrows56d b) | |
* __mrows56d _mmrows56_fmadd_pd (__mrows56d a, __mrows56d b, __mrows56d c) | |
* __mrows56d _mmrows56_mul_pd (__mrows56d a, __mrows56d b) | |
* __mrows56d _mmrows56_cmp_pd (__mrows56d a, __mrows56d b, const int imm8) | |
* __mrows56d _mmrows56_and_pd (__mrows56d a, __mrows56d b) | |
* __mrows56d _mmrows56_max_pd (__mrows56d a, __mrows56d b) | |
*/ | |
/* Generates a random double between low and high */ | |
double rand_double(double low, double high) | |
{ | |
double range = (high - low); | |
double div = RAND_MAX / range; | |
return low + (rand() / div); | |
} | |
/* Generates a random matrix */ | |
void rand_matrix(matrix *result, unsigned int seed, double low, double high) | |
{ | |
srand(seed); | |
for (int i = 0; i < result->rows; i++) | |
{ | |
for (int j = 0; j < result->cols; j++) | |
{ | |
set(result, i, j, rand_double(low, high)); | |
} | |
} | |
} | |
/* | |
* Allocates space for a matrix struct pointed to by the double pointer mat with | |
* `rows` rows and `cols` columns. You should also allocate memory for the data array | |
* and initialize all entries to be zeros. `parent` should be set to NULL to indicate that | |
* this matrix is not a slice. You should also set `ref_cnt` to 1. | |
* You should return -1 if either `rows` or `cols` or both have invalid values, or if any | |
* call to allocate memory in this function fails. If you don't set python error messages here upon | |
* failure, then remember to set it in numc.c. | |
* Return 0 upon success. | |
*/ | |
int allocate_matrix(matrix **mat, int rows, int cols) | |
{ | |
if (rows <= 0 || cols <= 0) | |
{ | |
return -1; | |
} | |
(*mat) = calloc(1, sizeof(matrix)); | |
if (!(*mat)) | |
{ | |
return -1; | |
} | |
(*mat)->data = (double *)calloc((rows * cols), sizeof(double)); | |
if (!((*mat)->data)) | |
{ | |
return -1; | |
} | |
(*mat)->rows = rows; | |
(*mat)->cols = cols; | |
(*mat)->parent = NULL; | |
(*mat)->ref_cnt = 1; | |
return 0; | |
} | |
/* | |
* Allocates space for a matrix struct pointed to by `mat` with `rows` rows and `cols` columns. | |
* Its data should point to the `offset`th entry of `from`'s data (you do not need to allocate memory) | |
* for the data field. `parent` should be set to `from` to indicate this matrix is a slice of `from`. | |
* You should return -1 if either `rows` or `cols` or both are non-positive or if any | |
* call to allocate memory in this function fails. | |
* If you don't set python error messages here upon failure, then remember to set it in numc.c. | |
* Return 0 upon success. | |
*/ | |
int allocate_matrix_ref(matrix **mat, matrix *from, int offset, int rows, int cols) | |
{ | |
if (rows <= 0 || cols <= 0) | |
{ | |
return -1; | |
} | |
*mat = calloc(1, sizeof(matrix)); | |
if ((*mat) == NULL) | |
{ | |
return -1; | |
} | |
if (from) | |
{ | |
(*mat)->data = &(from->data[offset]); | |
from->ref_cnt += 1; | |
(*mat)->rows = rows; | |
(*mat)->cols = cols; | |
(*mat)->parent = from; | |
(*mat)->ref_cnt = 1; | |
return 0; | |
} | |
else | |
{ | |
return -1; | |
} | |
} | |
/* | |
* You need to make sure that you only free `mat->data` if `mat` is not a slice and has no existing slices, | |
* or if `mat` is the last existing slice of its parent matrix and its parent matrix has no other references | |
* (including itself). You cannot assume that mat is not NULL. | |
*/ | |
void deallocate_matrix(matrix *mat) | |
{ | |
/* pseudocode: (1) check if mat is not a slice & mat has no existing slices | |
(rows) mat is the last slice of its parent & parent has no other references (3) mat is null | |
*/ | |
if (mat == NULL) | |
{ // if mat is null | |
return; | |
} | |
mat->ref_cnt -= 1; | |
if (mat->ref_cnt <= 0) | |
{ | |
deallocate_matrix(mat->parent); | |
if (!mat->parent && mat->data) | |
{ | |
free(mat->data); | |
} | |
free(mat); | |
} | |
} | |
/* | |
* Returns the double value of the matrix at the given row and column. | |
* You may assume `row` and `col` are valid. | |
*/ | |
double get(matrix *mat, int row, int col) | |
{ | |
if (!mat) { | |
return; | |
} | |
return (mat->data)[(row * mat->cols) + col]; | |
} | |
/* | |
* Sets the value at the given row and column to val. You may assume `row` and | |
* `col` are valid | |
*/ | |
void set(matrix *mat, int row, int col, double val) | |
{ | |
if (!mat) { | |
return; | |
} | |
(mat->data)[(row * (mat->cols)) + col] = val; | |
} | |
/* | |
* Sets all entries in mat to val | |
*/ | |
void fill_matrix(matrix *mat, double val) | |
{ | |
/* to optimize: | |
(1) unroll | |
(2) load pointer at current mat | |
(3) set pointer to val | |
(4) store pointer back in mat | |
*/ | |
int cols = mat->cols; | |
int rows = mat->rows; | |
__m256d newVal = _mm256_set1_pd(val); | |
#pragma omp parallel for | |
for (int i = 0; i < rows*cols / 4*4; i+=4) | |
{ | |
_mm256_storeu_pd(mat->data + i, newVal); | |
} | |
#pragma omp parallel for | |
for (int i = rows*cols / 4*4; i < rows*cols; i+=1) | |
{ | |
mat->data[i] = val; | |
} | |
} | |
/* | |
* Store the result of adding mat1 and mat2 to `result`. | |
* Return 0 upon success and a nonzero value upon failure. | |
*/ | |
int add_matrix(matrix *result, matrix *mat1, matrix *mat2) | |
{ | |
/* to optimize: | |
(1) load current item from mat1 | |
(2) load current item mat2 | |
(3) add 1 & 2 | |
(4) store sum in result | |
(5) tail case | |
*/ | |
int cols1 = mat1->cols; | |
int cols2 = mat2->cols; | |
int rows1 = mat1->rows; | |
int rows2 = mat2->rows; | |
if (rows1 != rows2 || cols1 != cols2 || cols1 <= 0 || cols2 <= 0 || rows1 <= 0 || rows2 <= 0) | |
{ | |
return 1; | |
} | |
#pragma omp parallel for | |
for (int i = 0; i < rows1; i++) | |
{ | |
for (int j = 0; j < cols1/(4*4); j += 4) | |
{ | |
__m256d v1 = _mm256_set_pd(mat1->data[i * cols1 + j + 3], mat1->data[i * cols1 + j + 2], mat1->data[i * cols1 + j + 1], mat1->data[i * cols1 + j]); | |
__m256d v2 = _mm256_set_pd(mat2->data[i * cols1 + j + 3], mat2->data[i * cols1 + j + 2], mat2->data[i * cols1 + j + 1], mat2->data[i * cols1 + j]); | |
__m256d sum = _mm256_add_pd(v1, v2); | |
_mm256_storeu_pd(result->data + i * cols1 + j, sum); | |
} | |
for (int j = cols1/(4*4); j < cols1; j += 1) { | |
result->data[i*cols1+j] = mat1->data[i*cols1+j] + mat2->data[i*cols1+j]; | |
} | |
} | |
return 0; | |
} | |
/* | |
* Store the result of subtracting mat2 from mat1 to `result`. | |
* Return 0 upon success and a nonzero value upon failure. | |
*/ | |
int sub_matrix(matrix *result, matrix *mat1, matrix *mat2) | |
{ | |
int cols1 = mat1->cols; | |
int cols2 = mat2->cols; | |
int rows1 = mat1->rows; | |
int rows2 = mat2->rows; | |
if ((mat1->rows) != (mat2->rows) || (mat1->cols) != (mat2->cols) || cols1 <= 0 || cols2 <= 0 || rows1 <= 0 || rows2 <= 0) | |
{ | |
return 1; | |
} | |
#pragma omp parallel for | |
for (int i = 0; i < rows1; i++) | |
{ | |
for (int j = 0; j < cols1/(4*4); j += 4) | |
{ | |
__m256d v1 = _mm256_set_pd(mat1->data[i * cols1 + j + 3], mat1->data[i * cols1 + j + 2], mat1->data[i * cols1 + j + 1], mat1->data[i * cols1 + j]); | |
__m256d v2 = _mm256_set_pd(mat2->data[i * cols1 + j + 3], mat2->data[i * cols1 + j + 2], mat2->data[i * cols1 + j + 1], mat2->data[i * cols1 + j]); | |
__m256d diff = _mm256_sub_pd(v1, v2); | |
_mm256_storeu_pd(result->data + i * cols1 + j, diff); | |
} | |
for (int j = cols1/(4*4); j < cols1; j += 1) { | |
result->data[i*cols1+j] = mat1->data[i*cols1+j] - mat2->data[i*cols1+j]; | |
} | |
} | |
return 0; | |
} | |
/* | |
* Store the result of multiplying mat1 and mat2 to `result`. | |
* Return 0 upon success and a nonzero value upon failure. | |
* Remember that matrix multiplication is not the same as multiplying individual elements. | |
*/ | |
int mul_matrix(matrix *result, matrix *mat1, matrix *mat2) | |
{ | |
if (!result || !mat1 || !mat2) { | |
return -1; | |
} | |
int cols1 = mat1->cols; | |
int cols2 = mat2->cols; | |
int rows1 = mat1->rows; | |
int rows2 = mat2->rows; | |
int area = rows1 * cols2; | |
if (cols1 != rows2 || cols1 <= 0 || cols2 <= 0 || rows1 <= 0 || rows2 <= 0) { | |
return -1; | |
} | |
int index = 0; | |
/* make transpose of mat2 */ | |
double *transpose = (double*) calloc(rows2 * cols2, sizeof(double)); | |
if (transpose == NULL) { | |
return -1; | |
} | |
for (int i = 0; i < cols2; i ++) { | |
for (int j = 0; j < rows2/4*4; j+=4) { | |
__m256d newVal = _mm256_set_pd(mat2->data[(j+3)*cols2+i], mat2->data[(j+2)*cols2+i], mat2->data[(j+1)*cols2+i], mat2->data[j*cols2+i]); | |
_mm256_storeu_pd(transpose+index, newVal); | |
index +=4; | |
} | |
for (int j = rows2/4*4; j < rows2; j+=1) { | |
transpose[index] = mat2->data[j*cols2+i]; | |
index +=1; | |
} | |
} | |
/* done w transpose, begin multiplication */ | |
for (int i = 0; i < area; i++) { | |
double *data = (double*) (mat1->data) + ((i/rows1)*cols1); | |
double *t_ptr = (double*) transpose + ((i%cols2)*rows2); | |
for (int j = 0; j < cols1/4*4; j+=4) { | |
__m256d sum = _mm256_loadu_pd((result->data)+i); | |
__m256d m_vals = _mm256_set_pd(data[j+3], data[j+2], data[j+1], data[j]); | |
__m256d t_vals = _mm256_set_pd(t_ptr[j+3], t_ptr[j+2], t_ptr[j+1], t_ptr[j]); | |
__m256d product = _mm256_mul_pd(t_vals, m_vals); | |
sum = _mm256_add_pd(sum, product); | |
_mm256_storeu_pd((result->data)+i, sum); | |
} | |
for (int j = cols1/4*4; j<cols1; j++) { | |
result->data[i] += data[j] * t_ptr[j]; | |
} | |
} | |
free(transpose); | |
return 0; | |
} | |
/* | |
* Store the result of raising mat to the (pow)th power to `result`. | |
* Return 0 upon success and a nonzero value upon failure. | |
* Remember that pow is defined with matrix multiplication, not element-wise multiplication. | |
*/ | |
int pow_matrix(matrix *result, matrix *mat, int pow) | |
{ | |
int side = mat -> rows; | |
int area = side * side; | |
if (pow == 0) { | |
/* make identity matrix */ | |
#pragma omp parallel for | |
for (int i = 0; i < side/8*8; i += 8) { | |
result->data[i * side + i] = 1; | |
result->data[(i+1) * side + (i+1)] = 1; | |
result->data[(i+2) * side + (i+2)] = 1; | |
result->data[(i+3) * side + (i+3)] = 1; | |
result->data[(i+4) * side + (i+4)] = 1; | |
result->data[(i+5) * side + (i+5)] = 1; | |
result->data[(i+6) * side + (i+6)] = 1; | |
result->data[(i+7) * side + (i+7)] = 1; | |
} | |
#pragma omp parallel for | |
for (int i = side/8*8; i < side; i ++) { | |
result->data[i * side + i] = 1; | |
} | |
if (!result -> data) { | |
return -1; | |
} | |
return 0; | |
} | |
if (pow == 1) { | |
memcpy(result->data, mat->data, sizeof(double) * area); | |
return 0; | |
} | |
matrix* iter = NULL; | |
allocate_matrix(&iter, side, side); | |
/* matrix multiplied */ | |
while(pow > 0) { | |
if (pow % 2 == 1) { | |
mul_matrix(result, mat, mat); | |
} | |
mul_matrix(iter, mat, mat); | |
memcpy(mat->data, iter->data, sizeof(double)*area); | |
pow = pow/2; | |
} | |
return 0; | |
} | |
/* | |
* Store the result of element-wise negating mat's entries to `result`. | |
* Return 0 upon success and a nonzero value upon failure. | |
*/ | |
int neg_matrix(matrix *result, matrix *mat) | |
{ | |
/* to optimize: | |
(1) load current item from mat | |
(2) multiply 1 by -1 | |
(3) store product in result | |
*/ | |
int cols = mat->cols; | |
#pragma omp parallel for | |
for (int i = 0; i < mat->rows; i++) | |
{ | |
for (int j = 0; j < cols/(4*4); j += 4) | |
{ | |
__m256d v1 = _mm256_set_pd(mat->data[i * cols + j + 3], mat->data[i * cols + j + 2], mat->data[i * cols + j + 1], mat->data[i * cols + j]); | |
__m256d negated = _mm256_mul_pd(v1, _mm256_set1_pd(-1)); | |
_mm256_storeu_pd((result->data) + i * cols + j, negated); | |
} | |
for (int j = cols/(4*4); j < cols; j += 1) { | |
result->data[i*cols+j] = mat->data[i*cols+j] * -1 ; | |
} | |
} | |
return 0; | |
} | |
/* | |
* Store the result of taking the absolute value element-wise to `result`. | |
* Return 0 upon success and a nonzero value upon failure. | |
*/ | |
int abs_matrix(matrix *result, matrix *mat) | |
{ | |
/* to optimize: | |
(1) load current item from mat | |
(2) negate item | |
(3) take max of item & its negation | |
(4) store max in result | |
*/ | |
int cols = mat->cols; | |
#pragma omp parallel for | |
for (int i = 0; i < mat->rows; i++) | |
{ | |
for (int j = 0; j < cols/(4*4); j += 4) | |
{ | |
__m256d v1 = _mm256_set_pd(mat->data[i * cols + j + 3], mat->data[i * cols + j + 2], mat->data[i * cols + j + 1], mat->data[i * cols + j]); | |
__m256d negated = _mm256_mul_pd(v1, _mm256_set1_pd(-1)); | |
__m256d maximum = _mm256_max_pd(v1, negated); | |
_mm256_storeu_pd((result->data) + i * cols + j, maximum); | |
} | |
for (int j = cols/(4*4); j <= cols; j += 1) { | |
double curr = mat->data[i*cols+j]; | |
if (curr < 0) { | |
curr = curr * -1; | |
} | |
result->data[i*cols+j] = curr; | |
} | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "numc.h" | |
#include <structmember.h> | |
static PyTypeObject Matrix61cType; | |
/* Helper functions for initalization of matrices and vectors */ | |
/* Matrix(rows, cols, low, high). Fill a matrix random double values */ | |
static int init_rand(PyObject *self, int rows, int cols, unsigned int seed, double low, double high) { | |
matrix *new_mat; | |
int alloc_failed = allocate_matrix(&new_mat, rows, cols); | |
if (alloc_failed) | |
return alloc_failed; | |
rand_matrix(new_mat, seed, low, high); | |
((Matrix61c *)self)->mat = new_mat; | |
((Matrix61c *)self)->shape = PyTuple_Pack(2, PyLong_FromLong(rows), PyLong_FromLong(cols)); | |
return 0; | |
} | |
/* Matrix(rows, cols, val). Fill a matrix of dimension rows * cols with val*/ | |
static int init_fill(PyObject *self, int rows, int cols, double val) { | |
matrix *new_mat; | |
int alloc_failed = allocate_matrix(&new_mat, rows, cols); | |
if (alloc_failed) | |
return alloc_failed; | |
else { | |
fill_matrix(new_mat, val); | |
((Matrix61c *)self)->mat = new_mat; | |
((Matrix61c *)self)->shape = PyTuple_Pack(2, PyLong_FromLong(rows), PyLong_FromLong(cols)); | |
} | |
return 0; | |
} | |
/* Matrix(rows, cols, 1d_list). Fill a matrix with dimension rows * cols with 1d_list values */ | |
static int init_1d(PyObject *self, int rows, int cols, PyObject *lst) { | |
if (rows * cols != PyList_Size(lst)) { | |
PyErr_SetString(PyExc_TypeError, "Incorrect number of elements in list"); | |
return -1; | |
} | |
matrix *new_mat; | |
int alloc_failed = allocate_matrix(&new_mat, rows, cols); | |
if (alloc_failed) | |
return alloc_failed; | |
int count = 0; | |
for (int i = 0; i < rows; i++) { | |
for (int j = 0; j < cols; j++) { | |
set(new_mat, i, j, PyFloat_AsDouble(PyList_GetItem(lst, count))); | |
count++; | |
} | |
} | |
((Matrix61c *)self)->mat = new_mat; | |
((Matrix61c *)self)->shape = PyTuple_Pack(2, PyLong_FromLong(rows), PyLong_FromLong(cols)); | |
return 0; | |
} | |
/* Matrix(2d_list). Fill a matrix with dimension len(2d_list) * len(2d_list[0]) */ | |
static int init_2d(PyObject *self, PyObject *lst) { | |
int rows = PyList_Size(lst); | |
if (rows == 0) { | |
PyErr_SetString(PyExc_TypeError, "Cannot initialize numc.Matrix with an empty list"); | |
return -1; | |
} | |
int cols; | |
if (!PyList_Check(PyList_GetItem(lst, 0))) { | |
PyErr_SetString(PyExc_TypeError, "List values not valid"); | |
return -1; | |
} else { | |
cols = PyList_Size(PyList_GetItem(lst, 0)); | |
} | |
for (int i = 0; i < rows; i++) { | |
if (!PyList_Check(PyList_GetItem(lst, i)) || PyList_Size(PyList_GetItem(lst, i)) != cols) { | |
PyErr_SetString(PyExc_TypeError, "List values not valid"); | |
return -1; | |
} | |
} | |
matrix *new_mat; | |
int alloc_failed = allocate_matrix(&new_mat, rows, cols); | |
if (alloc_failed) | |
return alloc_failed; | |
for (int i = 0; i < rows; i++) { | |
for (int j = 0; j < cols; j++) { | |
set(new_mat, i, j, PyFloat_AsDouble(PyList_GetItem(PyList_GetItem(lst, i), j))); | |
} | |
} | |
((Matrix61c *)self)->mat = new_mat; | |
((Matrix61c *)self)->shape = PyTuple_Pack(2, PyLong_FromLong(rows), PyLong_FromLong(cols)); | |
return 0; | |
} | |
/* This deallocation function is called when reference count is 0*/ | |
static void Matrix61c_dealloc(Matrix61c *self) { | |
deallocate_matrix(self->mat); | |
Py_TYPE(self)->tp_free(self); | |
} | |
/* For immutable types all initializations should take place in tp_new */ | |
static PyObject *Matrix61c_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { | |
/* size of allocated memory is tp_basicsize + nitems*tp_itemsize*/ | |
Matrix61c *self = (Matrix61c *)type->tp_alloc(type, 0); | |
return (PyObject *)self; | |
} | |
/* This matrix61c type is mutable, so needs init function. Return 0 on success otherwise -1 */ | |
static int Matrix61c_init(PyObject *self, PyObject *args, PyObject *kwds) { | |
/* Generate random matrices */ | |
if (kwds != NULL) { | |
PyObject *rand = PyDict_GetItemString(kwds, "rand"); | |
if (!rand) { | |
PyErr_SetString(PyExc_TypeError, "Invalid arguments"); | |
return -1; | |
} | |
if (!PyBool_Check(rand)) { | |
PyErr_SetString(PyExc_TypeError, "Invalid arguments"); | |
return -1; | |
} | |
if (rand != Py_True) { | |
PyErr_SetString(PyExc_TypeError, "Invalid arguments"); | |
return -1; | |
} | |
PyObject *low = PyDict_GetItemString(kwds, "low"); | |
PyObject *high = PyDict_GetItemString(kwds, "high"); | |
PyObject *seed = PyDict_GetItemString(kwds, "seed"); | |
double double_low = 0; | |
double double_high = 1; | |
unsigned int unsigned_seed = 0; | |
if (low) { | |
if (PyFloat_Check(low)) { | |
double_low = PyFloat_AsDouble(low); | |
} else if (PyLong_Check(low)) { | |
double_low = PyLong_AsLong(low); | |
} | |
} | |
if (high) { | |
if (PyFloat_Check(high)) { | |
double_high = PyFloat_AsDouble(high); | |
} else if (PyLong_Check(high)) { | |
double_high = PyLong_AsLong(high); | |
} | |
} | |
if (double_low >= double_high) { | |
PyErr_SetString(PyExc_TypeError, "Invalid arguments"); | |
return -1; | |
} | |
// Set seed if argument exists | |
if (seed) { | |
if (PyLong_Check(seed)) { | |
unsigned_seed = PyLong_AsUnsignedLong(seed); | |
} | |
} | |
PyObject *rows = NULL; | |
PyObject *cols = NULL; | |
if (PyArg_UnpackTuple(args, "args", 2, 2, &rows, &cols)) { | |
if (rows && cols && PyLong_Check(rows) && PyLong_Check(cols)) { | |
return init_rand(self, PyLong_AsLong(rows), PyLong_AsLong(cols), unsigned_seed, double_low, double_high); | |
} | |
} else { | |
PyErr_SetString(PyExc_TypeError, "Invalid arguments"); | |
return -1; | |
} | |
} | |
PyObject *arg1 = NULL; | |
PyObject *arg2 = NULL; | |
PyObject *arg3 = NULL; | |
if (PyArg_UnpackTuple(args, "args", 1, 3, &arg1, &arg2, &arg3)) { | |
/* arguments are (rows, cols, val) */ | |
if (arg1 && arg2 && arg3 && PyLong_Check(arg1) && PyLong_Check(arg2) && (PyLong_Check(arg3) || PyFloat_Check(arg3))) { | |
if (PyLong_Check(arg3)) { | |
return init_fill(self, PyLong_AsLong(arg1), PyLong_AsLong(arg2), PyLong_AsLong(arg3)); | |
} | |
else | |
return init_fill(self, PyLong_AsLong(arg1), PyLong_AsLong(arg2), PyFloat_AsDouble(arg3)); | |
} else if (arg1 && arg2 && arg3 && PyLong_Check(arg1) && PyLong_Check(arg2) && PyList_Check(arg3)) { | |
/* Matrix(rows, cols, 1D list) */ | |
return init_1d(self, PyLong_AsLong(arg1), PyLong_AsLong(arg2), arg3); | |
} else if (arg1 && PyList_Check(arg1) && arg2 == NULL && arg3 == NULL) { | |
/* Matrix(rows, cols, 1D list) */ | |
return init_2d(self, arg1); | |
} else if (arg1 && arg2 && PyLong_Check(arg1) && PyLong_Check(arg2) && arg3 == NULL) { | |
/* Matrix(rows, cols, 1D list) */ | |
return init_fill(self, PyLong_AsLong(arg1), PyLong_AsLong(arg2), 0); | |
} else { | |
PyErr_SetString(PyExc_TypeError, "Invalid arguments"); | |
return -1; | |
} | |
} else { | |
PyErr_SetString(PyExc_TypeError, "Invalid arguments"); | |
return -1; | |
} | |
} | |
/* List of lists representations for matrices */ | |
static PyObject *Matrix61c_to_list(Matrix61c *self) { | |
int rows = self->mat->rows; | |
int cols = self->mat->cols; | |
PyObject *py_lst = PyList_New(rows); | |
for (int i = 0; i < rows; i++) { | |
PyList_SetItem(py_lst, i, PyList_New(cols)); | |
PyObject *curr_row = PyList_GetItem(py_lst, i); | |
for (int j = 0; j < cols; j++) { | |
PyList_SetItem(curr_row, j, PyFloat_FromDouble(get(self->mat, i, j))); | |
} | |
} | |
return py_lst; | |
} | |
static PyObject *Matrix61c_class_to_list(Matrix61c *self, PyObject *args) { | |
PyObject *mat = NULL; | |
if (PyArg_UnpackTuple(args, "args", 1, 1, &mat)) { | |
if (!PyObject_TypeCheck(mat, &Matrix61cType)) { | |
PyErr_SetString(PyExc_TypeError, "Argument must of type numc.Matrix!"); | |
return NULL; | |
} | |
Matrix61c* mat61c = (Matrix61c*)mat; | |
return Matrix61c_to_list(mat61c); | |
} else { | |
PyErr_SetString(PyExc_TypeError, "Invalid arguments"); | |
return NULL; | |
} | |
} | |
/* Add class methods */ | |
static PyMethodDef Matrix61c_class_methods[] = { | |
{"to_list", (PyCFunction)Matrix61c_class_to_list, METH_VARARGS, "Returns a list representation of numc.Matrix"}, | |
{NULL, NULL, 0, NULL} | |
}; | |
/* Matrix61c string representation. For printing purposes. */ | |
static PyObject *Matrix61c_repr(PyObject *self) { | |
PyObject *py_lst = Matrix61c_to_list((Matrix61c *)self); | |
return PyObject_Repr(py_lst); | |
} | |
/* For __getitem__. (e.g. mat[0]) */ | |
static PyObject *Matrix61c_subscript(Matrix61c* self, PyObject* key) { | |
if (!PyLong_Check(key)) { | |
PyErr_SetString(PyExc_TypeError, "Key is not valid"); | |
return NULL; | |
} | |
int index = PyLong_AsLong(key); | |
if (index >= self->mat->rows || index < 0) { | |
PyErr_SetString(PyExc_IndexError, "Index out of range"); | |
return NULL; | |
} | |
if (self->mat->cols == 1) { // if one single number, unwrap from list | |
return PyFloat_FromDouble(get(self->mat, index, 0)); | |
} | |
matrix *new_mat; | |
int ref_failed = allocate_matrix_ref(&new_mat, self->mat, index * self->mat->cols, self->mat->cols, 1); | |
if (ref_failed != 0) { | |
PyErr_SetString(PyExc_RuntimeError, "Failed to allocate slice"); | |
return NULL; | |
} | |
Matrix61c* rv = (Matrix61c*) Matrix61c_new(&Matrix61cType, NULL, NULL); | |
rv->mat = new_mat; | |
rv->shape = PyTuple_Pack(2, PyLong_FromLong(new_mat->rows), PyLong_FromLong(1)); | |
return (PyObject*)rv; | |
} | |
/* For __setitem__ (e.g. mat[0] = 1) */ | |
static int Matrix61c_set_subscript(Matrix61c* self, PyObject *key, PyObject *v) { | |
if (!PyLong_Check(key)) { | |
PyErr_SetString(PyExc_TypeError, "Key is not valid"); | |
return -1; | |
} | |
int index = PyLong_AsLong(key); | |
if (index >= self->mat->rows || index < 0) { | |
PyErr_SetString(PyExc_IndexError, "Index out of range"); | |
return -1; | |
} | |
int cols = self->mat->cols; | |
if (cols == 1) { | |
if (!PyFloat_Check(v) && !PyLong_Check(v)) { | |
PyErr_SetString(PyExc_TypeError, "Value is not valid"); | |
return -1; | |
} | |
double val = PyFloat_AsDouble(v); | |
set(self->mat, index, 0, val); | |
return 0; | |
} else { | |
if (!PyList_Check(v)) { | |
PyErr_SetString(PyExc_TypeError, "Value is not valid"); | |
return -1; | |
} | |
for (int i = 0; i < cols; i++) { | |
if (!PyFloat_Check(PyList_GetItem(v, i)) && !PyLong_Check(PyList_GetItem(v, i))) { | |
PyErr_SetString(PyExc_TypeError, "Value is not valid"); | |
return -1; | |
} | |
set(self->mat, index, i, PyFloat_AsDouble(PyList_GetItem(v, i))); | |
} | |
return 0; | |
} | |
return -1; | |
} | |
static PyMappingMethods Matrix61c_mapping = { | |
// NULL, | |
.mp_subscript = (binaryfunc) Matrix61c_subscript, | |
.mp_ass_subscript= (objobjargproc) Matrix61c_set_subscript, | |
}; | |
/* NUMBER METHODS */ | |
/* | |
* Add the second numc.Matrix (Matrix61c) object to the first one. The first operand is | |
* self, and the second operand can be obtained by casting `args`. | |
*/ | |
static PyObject *Matrix61c_add(Matrix61c* self, PyObject* args) { | |
if (!PyObject_TypeCheck(self, &Matrix61cType) || !PyObject_TypeCheck(args, &Matrix61cType)) { | |
PyErr_SetString(PyExc_TypeError, "Entry must be a numc.matrix."); | |
return NULL; | |
} | |
Matrix61c* argMat = (Matrix61c*) args; | |
if ((self->mat->rows != argMat->mat->rows) | |
|| (self->mat->cols != argMat->mat->cols)) { | |
PyErr_SetString(PyExc_ValueError, "Matrices must have equal dimensions."); | |
return NULL; | |
} | |
Matrix61c* result = (Matrix61c*) Matrix61c_new(&Matrix61cType, NULL, NULL); | |
result -> shape = PyTuple_Pack(2, PyLong_FromLong(self->mat->rows), PyLong_FromLong(self->mat->cols)); | |
int err = allocate_matrix(&(result->mat), self->mat->rows, self->mat->cols); | |
int err1 = add_matrix((result->mat), self->mat, argMat -> mat); | |
if (err != 0 || err1 != 0) { | |
PyErr_SetString(PyExc_RuntimeError, "Allocation runtime error has occured"); | |
return NULL; | |
} | |
return (PyObject*) result; | |
} | |
/* | |
* Substract the second numc.Matrix (Matrix61c) object from the first one. The first operand is | |
* self, and the second operand can be obtained by casting `args`. | |
*/ | |
static PyObject *Matrix61c_sub(Matrix61c* self, PyObject* args) { | |
if (!PyObject_TypeCheck(self, &Matrix61cType) || !PyObject_TypeCheck(args, &Matrix61cType)) { | |
PyErr_SetString(PyExc_TypeError, "Entry must be a numc.matrix."); | |
return NULL; | |
} | |
Matrix61c* argMat = (Matrix61c*) args; | |
if ((self->mat->rows != argMat->mat->rows) | |
|| (self->mat->cols != argMat->mat->cols)) { | |
PyErr_SetString(PyExc_ValueError, "Matrices must have equal dimensions."); | |
return NULL; | |
} | |
Matrix61c* result = (Matrix61c*) Matrix61c_new(&Matrix61cType, NULL, NULL); | |
result -> shape = PyTuple_Pack(2, PyLong_FromLong(self->mat->rows), PyLong_FromLong(self->mat->cols)); | |
int err = allocate_matrix(&(result->mat), self->mat->rows, self->mat->cols); | |
int err1 = sub_matrix((result->mat), self->mat, argMat -> mat); | |
if (err != 0 || err1 != 0) { | |
PyErr_SetString(PyExc_RuntimeError, "Allocation runtime error has occured"); | |
return NULL; | |
} | |
return (PyObject*) result; | |
} | |
/* | |
* NOT element-wise multiplication. The first operand is self, and the second operand | |
* can be obtained by casting `args`. | |
*/ | |
static PyObject *Matrix61c_multiply(Matrix61c* self, PyObject *args) { | |
if (!PyObject_TypeCheck(self, &Matrix61cType) || !PyObject_TypeCheck(args, &Matrix61cType)) { | |
PyErr_SetString(PyExc_TypeError, "Entry must be a numc.matrix."); | |
return NULL; | |
} | |
Matrix61c* argMat = (Matrix61c*) args; | |
if ((self->mat->cols != argMat->mat->rows)) { | |
PyErr_SetString(PyExc_ValueError, "Matrix A's number of columns must be equal to matrix B's number of rows!"); | |
return NULL; | |
} | |
Matrix61c* result = (Matrix61c*) Matrix61c_new(&Matrix61cType, NULL, NULL); | |
result -> shape = PyTuple_Pack(2, PyLong_FromLong(self->mat->rows), PyLong_FromLong(argMat->mat->cols)); | |
int err = allocate_matrix(&(result->mat), self->mat->rows, argMat->mat->cols); | |
int err1 = mul_matrix((result->mat), self->mat, argMat -> mat); | |
if (err != 0 || err1 != 0) { | |
PyErr_SetString(PyExc_RuntimeError, "Allocation runtime error has occured"); | |
return NULL; | |
} | |
return (PyObject*) result; | |
} | |
/* | |
* Negates the given numc.Matrix. | |
*/ | |
static PyObject *Matrix61c_neg(Matrix61c* self) { | |
Matrix61c* result = (Matrix61c*) Matrix61c_new(&Matrix61cType, NULL, NULL); | |
result -> shape = PyTuple_Pack(2, PyLong_FromLong(self->mat->rows), PyLong_FromLong(self->mat->cols)); | |
int err = allocate_matrix(&(result->mat), self->mat->rows, self->mat->cols); | |
int err1 = neg_matrix((result->mat), self->mat); | |
if (err != 0 || err1 != 0) { | |
PyErr_SetString(PyExc_RuntimeError, "Allocation runtime error has occured"); | |
return NULL; | |
} | |
return (PyObject*) result; | |
} | |
/* | |
* Take the element-wise absolute value of this numc.Matrix. | |
*/ | |
static PyObject *Matrix61c_abs(Matrix61c *self) { | |
Matrix61c* result = (Matrix61c*) Matrix61c_new(&Matrix61cType, NULL, NULL); | |
result -> shape = PyTuple_Pack(2, PyLong_FromLong(self->mat->rows), PyLong_FromLong(self->mat->cols)); | |
int err = allocate_matrix(&(result->mat), self->mat->rows, self->mat->cols); | |
int err1 = abs_matrix((result->mat), self->mat); | |
if (err != 0 || err1 != 0) { | |
PyErr_SetString(PyExc_RuntimeError, "Allocation runtime error has occured"); | |
return NULL; | |
} | |
return (PyObject*) result; | |
} | |
/* | |
* Raise numc.Matrix (Matrix61c) to the `pow`th power. You can ignore the argument `optional`. | |
*/ | |
static PyObject *Matrix61c_pow(Matrix61c *self, PyObject *pow, PyObject *optional) { | |
if (!PyObject_TypeCheck(self, &Matrix61cType)) { | |
PyErr_SetString(PyExc_TypeError, "Entry must be a numc.matrix."); | |
return NULL; | |
} | |
if (!PyLong_Check(pow)) { | |
PyErr_SetString(PyExc_TypeError, "Power must be an integer."); | |
return NULL; | |
} | |
int argPow = (int) PyLong_AsLong(pow); | |
if ((self->mat->cols != self->mat->rows)) { | |
PyErr_SetString(PyExc_ValueError, "The matrix must be square!"); | |
return NULL; | |
} | |
if (argPow < 0) { | |
PyErr_SetString(PyExc_ValueError, "Power must be greater than or equal to 0"); | |
return NULL; | |
} | |
Matrix61c* result = (Matrix61c*) Matrix61c_new(&Matrix61cType, NULL, NULL); | |
result -> shape = PyTuple_Pack(2, PyLong_FromLong(self->mat->rows), PyLong_FromLong(self->mat->cols)); | |
int err = allocate_matrix(&(result->mat), self->mat->rows, self->mat->cols); | |
int err1 = pow_matrix((result->mat), self->mat, argPow); | |
if (err != 0 || err1 != 0) { | |
PyErr_SetString(PyExc_RuntimeError, "Allocation runtime error has occured"); | |
return NULL; | |
} | |
return (PyObject*) result; | |
} | |
/* | |
* Create a PyNumberMethods struct for overloading operators with all the number methods you have | |
* defined. You might find this link helpful: https://docs.python.org/3.6/c-api/typeobj.html | |
*/ | |
static PyNumberMethods Matrix61c_as_number = { | |
.nb_add = (binaryfunc) Matrix61c_add, | |
.nb_subtract = (binaryfunc) Matrix61c_sub, | |
.nb_multiply = (binaryfunc) Matrix61c_multiply, | |
.nb_power = (ternaryfunc) Matrix61c_pow, | |
.nb_negative = (unaryfunc) Matrix61c_neg, | |
.nb_absolute = (unaryfunc) Matrix61c_abs, | |
}; | |
/* INSTANCE METHODS */ | |
/* | |
* Given a numc.Matrix self, parse `args` to (int) row, (int) col, and (double/int) val. | |
* This function should return None in Python. | |
*/ | |
static PyObject *Matrix61c_set_value(Matrix61c *self, PyObject* args) { | |
PyObject *argRow = NULL; | |
PyObject *argCol = NULL; | |
PyObject *argVal = NULL; | |
if (!PyObject_TypeCheck(self, &Matrix61cType)) { | |
PyErr_SetString(PyExc_TypeError, "Matrix must be a numc.Matrix"); | |
return Py_None; | |
} | |
if (!PyArg_UnpackTuple(args, "args", 3, 3, &argRow, &argCol, &argVal)) { | |
PyErr_SetString(PyExc_TypeError, "Must have 3 arguments in args"); | |
return Py_None; | |
} | |
if (!PyLong_Check(argRow) || !PyLong_Check(argCol)) { | |
PyErr_SetString(PyExc_TypeError, "Column & row must both be integers"); | |
return Py_None; | |
} | |
int row = (int) PyLong_AsLong(argRow); | |
int col = (int) PyLong_AsLong(argCol); | |
if (!PyFloat_Check(argVal) && !PyLong_Check(argVal)) { | |
PyErr_SetString(PyExc_TypeError, "Val must be either a float or integer"); | |
return Py_None; | |
} | |
if (row < 0 || row > self->mat->rows || col < 0 || col > self->mat->cols) { | |
PyErr_SetString(PyExc_ValueError, "Row or column is out of bounds"); | |
return Py_None; | |
} | |
double val = (double) PyFloat_AsDouble(argVal); | |
set(self->mat, row, col, val); | |
return Py_None; | |
} | |
/* | |
* Given a numc.Matrix `self`, parse `args` to (int) row and (int) col. | |
* This function should return the value at the `row`th row and `col`th column, which is a Python | |
* float. | |
*/ | |
static PyObject *Matrix61c_get_value(Matrix61c *self, PyObject* args) { | |
PyObject *argRow = NULL; | |
PyObject *argCol = NULL; | |
if (!PyObject_TypeCheck(self, &Matrix61cType)) { | |
PyErr_SetString(PyExc_TypeError, "Matrix must be a numc.Matrix"); | |
return Py_None; | |
} | |
if (!PyArg_UnpackTuple(args, "args", 2, 2, &argRow, &argCol)) { | |
PyErr_SetString(PyExc_TypeError, "There must be two arguments in args."); | |
return Py_None; | |
} | |
if (!PyLong_Check(argRow) || !PyLong_Check(argCol)) { | |
PyErr_SetString(PyExc_TypeError, "Row & column must be integers"); | |
return Py_None; | |
} | |
int row = (int) PyLong_AsLong(argRow); | |
int col = (int) PyLong_AsLong(argCol); | |
if (row < 0 || row >= self->mat->rows) { | |
PyErr_SetString(PyExc_TypeError, "Row is out of bounds"); | |
return Py_None; | |
} else if (col < 0 || col >= self->mat->cols) { | |
PyErr_SetString(PyExc_TypeError, "Col is out of bounds"); | |
return Py_None; | |
} | |
return PyFloat_FromDouble(get(self->mat, row, col)); | |
} | |
/* | |
* Create an array of PyMethodDef structs to hold the instance methods. | |
* Name the python function corresponding to Matrix61c_get_value as "get" and Matrix61c_set_value | |
* as "set" | |
* You might find this link helpful: https://docs.python.org/3.6/c-api/structures.html | |
*/ | |
static PyMethodDef Matrix61c_methods[] = { | |
{"set", (PyCFunction)Matrix61c_set_value, METH_VARARGS, "Sets row, column index to given value."}, | |
{"get", (PyCFunction)Matrix61c_get_value, METH_VARARGS, "Returns value at given row, index."}, | |
{NULL} /* Sentinel */ | |
}; | |
/* INSTANCE ATTRIBUTES*/ | |
static PyMemberDef Matrix61c_members[] = { | |
{"shape", T_OBJECT_EX, offsetof(Matrix61c, shape), 0, | |
"(rows, cols)"}, | |
{NULL} /* Sentinel */ | |
}; | |
static PyTypeObject Matrix61cType = { | |
PyVarObject_HEAD_INIT(NULL, 0) | |
.tp_name = "numc.Matrix", | |
.tp_basicsize = sizeof(Matrix61c), | |
.tp_dealloc = (destructor)Matrix61c_dealloc, | |
.tp_repr = (reprfunc)Matrix61c_repr, | |
.tp_as_number = &Matrix61c_as_number, | |
.tp_flags = Py_TPFLAGS_DEFAULT | | |
Py_TPFLAGS_BASETYPE, | |
.tp_doc = "numc.Matrix objects", | |
.tp_methods = Matrix61c_methods, | |
.tp_members = Matrix61c_members, | |
.tp_as_mapping = &Matrix61c_mapping, | |
.tp_init = (initproc)Matrix61c_init, | |
.tp_new = Matrix61c_new | |
}; | |
static struct PyModuleDef numcmodule = { | |
PyModuleDef_HEAD_INIT, | |
"numc", | |
"Numc matrix operations", | |
-1, | |
Matrix61c_class_methods | |
}; | |
/* Initialize the numc module */ | |
PyMODINIT_FUNC PyInit_numc(void) { | |
PyObject* m; | |
if (PyType_Ready(&Matrix61cType) < 0) | |
return NULL; | |
m = PyModule_Create(&numcmodule); | |
if (m == NULL) | |
return NULL; | |
Py_INCREF(&Matrix61cType); | |
PyModule_AddObject(m, "Matrix", (PyObject *)&Matrix61cType); | |
printf("CS61C Spring 2021 Project 4: numc imported!\n"); | |
fflush(stdout); | |
return m; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from utils import * | |
from unittest import TestCase | |
""" | |
- For each operation, you should write tests to test on matrices of different sizes. | |
- Keep in mind that the tests provided in the starter code are NOT comprehensive. That is, we strongly | |
advise you to modify them and add new tests. | |
- Hint: use dp_mc_matrix to generate dumbpy and numc matrices with the same data and use | |
cmp_dp_nc_matrix to compare the results | |
""" | |
class TestAdd(TestCase): | |
def test_small_add(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(2, 2, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(2, 2, seed=1) | |
print("CORRECT ", dp_mat1+dp_mat2) | |
print("NUMC ", nc_mat1+nc_mat2) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "add") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_medium_add(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(50, 50, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(50, 50, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "add") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_large_add(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(100, 100, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(100, 100, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "add") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_vlarge_add(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(5000, 5000, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(5000, 5000, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "add") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_rand_add(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(100, 100, 10, 1000, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(100, 100, 10, 5000, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "add") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_neg_add(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(300, 300, -1000, 0, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(300, 300, -2000, 0, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "add") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_typecheck_add(self): | |
try: | |
nc.Matrix(3, 3) + [2, 2] | |
self.assertTrue(False) | |
except TypeError as e: | |
print(e) | |
pass | |
def test_dims_add(self): | |
try: | |
nc.Matrix(3, 3) + nc.Matrix(5, 5) | |
self.assertTrue(False) | |
except ValueError as e: | |
print(e) | |
pass | |
class TestSub(TestCase): | |
def test_small_sub(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(2, 2, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(2, 2, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "sub") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_medium_sub(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(50, 50, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(50, 50, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "sub") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_large_sub(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(100, 100, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(100, 100, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "sub") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_rand_sub(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(100, 100, 10, 1000, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(100, 100, 10, 5000, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "sub") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_neg_sub(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(300, 300, -1000, 0, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(300, 300, -2000, 0, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "sub") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_typecheck_sub(self): | |
try: | |
nc.Matrix(3, 3) - [2, 2] | |
self.assertTrue(False) | |
except TypeError as e: | |
print(e) | |
pass | |
def test_dims_sub(self): | |
try: | |
nc.Matrix(3, 3) - nc.Matrix(5, 5) | |
self.assertTrue(False) | |
except ValueError as e: | |
print(e) | |
pass | |
class TestAbs(TestCase): | |
def test_small_abs(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(2, 2, -10, 0, seed=0) | |
is_correct, speed_up = compute([dp_mat], [nc_mat], "abs") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_medium_abs(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(50, 50, -50, -10, seed=0) | |
is_correct, speed_up = compute([dp_mat], [nc_mat], "abs") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_large_abs(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -50, 50, seed=0) | |
is_correct, speed_up = compute([dp_mat], [nc_mat], "abs") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_rand_abs(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(300, 300, -1000, 1000, seed=0) | |
is_correct, speed_up = compute([dp_mat], [nc_mat], "abs") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_neg_abs(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(300, 300, -100000, 0, seed=0) | |
is_correct, speed_up = compute([dp_mat], [nc_mat], "abs") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
class TestNeg(TestCase): | |
def test_small_neg(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(2, 2, seed=0) | |
is_correct, speed_up = compute([dp_mat], [nc_mat], "neg") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_medium_neg(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(50, 50, -50, -10, seed=0) | |
is_correct, speed_up = compute([dp_mat], [nc_mat], "neg") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_large_neg(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -50, 50, seed=0) | |
is_correct, speed_up = compute([dp_mat], [nc_mat], "neg") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_rand_neg(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(300, 300, -1000, 1000, seed=0) | |
is_correct, speed_up = compute([dp_mat], [nc_mat], "neg") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_neg_neg(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(300, 300, -100000, 0, seed=0) | |
is_correct, speed_up = compute([dp_mat], [nc_mat], "neg") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
class TestMul(TestCase): | |
def test_small_mul(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(2, 3, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(3, 2, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "mul") | |
if not(is_correct): | |
print("correct", dp_mat1 * dp_mat2) | |
print("NUMC ", nc_mat1 * nc_mat2) | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_medium_mul(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(5, 1, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(1, 5, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "mul") | |
if not(is_correct): | |
print("CORRECT 1", dp_mat1*dp_mat2) | |
print("NUMC ", nc_mat1 * nc_mat2) | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_large_mul(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(100, 100, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(100, 100, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "mul") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_rand_mul(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(100, 100, 10, 1000, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(100, 100, 10, 5000, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "mul") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_neg_mul(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(300, 300, -1000, 0, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(300, 300, -2000, 1000, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "mul") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_mul_dim_1(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(1, 1, -1000, 0, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(1, 1, -2000, 1000, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "mul") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_mul_dim_1000(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(1000, 1000, -1000, 0, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(1000, 1000, -2000, 1000, seed=1) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "mul") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
# def test_typecheck_mul(self): | |
# try: | |
# nc.Matrix(3, 3) * [2, 2] | |
# self.assertTrue(False) | |
# except TypeError as e: | |
# print(e) | |
# pass | |
# def test_dims_mul(self): | |
# try: | |
# nc.Matrix(3, 3) * nc.Matrix(5, 5) | |
# self.assertTrue(False) | |
# except ValueError as e: | |
# print(e) | |
# pass | |
class TestPow(TestCase): | |
def test_small_pow0to100(self): | |
for i in range(100): | |
print("Testing small to the power of " + str(i)) | |
dp_mat, nc_mat = rand_dp_nc_matrix(2, 2, -1000, 1000, seed=0) | |
is_correct, speed_up = compute([dp_mat, i], [nc_mat, i], "pow") | |
if not(is_correct): | |
print("CORRECT ", dp_mat ** i) | |
print("NUMC ", nc_mat ** i) | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_medium_pow0to100(self): | |
for i in range(100): | |
print("Testing medium to the power of " + str(i)) | |
dp_mat, nc_mat = rand_dp_nc_matrix(50, 50, -1000, 1000, seed=0) | |
is_correct, speed_up = compute([dp_mat, i], [nc_mat, i], "pow") | |
if not(is_correct): | |
print("CORRECT ", dp_mat ** i) | |
print("NUMC ", nc_mat ** i) | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_large_pow0to100(self): | |
for i in range(81): | |
print("Testing large to the power of " + str(i)) | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -1000, 1000, seed=0) | |
is_correct, speed_up = compute([dp_mat, i], [nc_mat, i], "pow") | |
if not(is_correct): | |
print("CORRECT ", dp_mat ** i) | |
print("NUMC ", nc_mat ** i) | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_changing_dims_0(self): | |
for i in range(1, 100): | |
for j in range (11): | |
print("Testing w dimensions of ", i, " to the power of ", j) | |
dp_mat, nc_mat = rand_dp_nc_matrix(i, i, -100000, 100000, seed=0) | |
is_correct, speed_up = compute([dp_mat, j], [nc_mat, j], "pow") | |
if not(is_correct): | |
print("ORIGINAL ", dp_mat) | |
print("CORRECT ", dp_mat ** j) | |
print("NUMC ", nc_mat ** j) | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_dim_1(self): | |
for i in range(1, 100): | |
dp_mat, nc_mat = rand_dp_nc_matrix(1, 1, -100000, 100000, seed=0) | |
is_correct, speed_up = compute([dp_mat, i], [nc_mat, i], "pow") | |
if not(is_correct): | |
print("CORRECT ", dp_mat ** j) | |
print("NUMC ", nc_mat ** j) | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_huge_pow0to20(self): | |
for i in range(20): | |
print("Testing huge to the power of " + str(i)) | |
dp_mat, nc_mat = rand_dp_nc_matrix(1000, 1000, seed=0) | |
is_correct, speed_up = compute([dp_mat, i], [nc_mat, i], "pow") | |
self.assertTrue(is_correct) | |
print_speedup(speed_up) | |
def test_typecheck_pow(self): | |
try: | |
[[1, 2, 3], [4, 5, 6]] ** 3 | |
self.assertTrue(False) | |
except TypeError as e: | |
print(e) | |
pass | |
def test_notnum_pow(self): | |
try: | |
nc.Matrix([[1, 2, 3], [4, 5, 6], [1, 2, 3]]) ** 5.49 | |
self.assertTrue(False) | |
except TypeError as e: | |
print(e) | |
pass | |
def test_notsquare_pow(self): | |
try: | |
nc.Matrix([[1, 2, 3], [4, 5, 6]]) ** 5 | |
self.assertTrue(False) | |
except ValueError as e: | |
print(e) | |
pass | |
def test_negpow_pow(self): | |
try: | |
nc.Matrix([[1, 2, 3], [4, 5, 6], [1, 2, 3]]) ** -5 | |
self.assertTrue(False) | |
except ValueError as e: | |
print(e) | |
pass | |
class TestGet(TestCase): | |
def test_get(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(2, 2, seed=0) | |
rand_row = np.random.randint(dp_mat.shape[0]) | |
rand_col = np.random.randint(dp_mat.shape[1]) | |
self.assertEqual(round(dp_mat.get(rand_row, rand_col), decimal_places), | |
round(nc_mat.get(rand_row, rand_col), decimal_places)) | |
def test_get_random(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -1000, 10000, seed=0) | |
rand_row = np.random.randint(dp_mat.shape[0]) | |
rand_col = np.random.randint(dp_mat.shape[1]) | |
self.assertEqual(round(dp_mat.get(rand_row, rand_col), decimal_places), | |
round(nc_mat.get(rand_row, rand_col), decimal_places)) | |
def test_get_materror(self): | |
try: | |
dp_mat = [[1, 2, 3], [4, 5, 6]] | |
dp_mat[0, 0] | |
self.assertTrue(False) | |
except TypeError as e: | |
print(e) | |
pass | |
def test_get_argserror(self): | |
try: | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -1000, 10000, seed=0) | |
dp_mat.get(0) | |
self.assertTrue(False) | |
except TypeError as e: | |
print(e) | |
pass | |
def test_get_rowcolerror(self): | |
try: | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -1000, 10000, seed=0) | |
dp_mat.get(9.5, 100.3) | |
self.assertTrue(False) | |
except TypeError as e: | |
print(e) | |
pass | |
def test_get_rowcolerror1(self): | |
try: | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -1000, 10000, seed=0) | |
dp_mat.get(200, 200) | |
self.assertTrue(False) | |
except IndexError as e: | |
print(e) | |
pass | |
class TestSet(TestCase): | |
def test_set(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(2, 2, seed=0) | |
rand_row = np.random.randint(dp_mat.shape[0]) | |
rand_col = np.random.randint(dp_mat.shape[1]) | |
dp_mat.set(rand_row, rand_col, 2) | |
nc_mat.set(rand_row, rand_col, 2) | |
self.assertTrue(cmp_dp_nc_matrix(dp_mat, nc_mat)) | |
def test_set_random(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(1000, 1000, 1000, 20000, seed=0) | |
rand_row = np.random.randint(dp_mat.shape[0]) | |
rand_col = np.random.randint(dp_mat.shape[1]) | |
dp_mat.set(rand_row, rand_col, 5) | |
nc_mat.set(rand_row, rand_col, 5) | |
self.assertTrue(cmp_dp_nc_matrix(dp_mat, nc_mat)) | |
def test_set_val(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -1000, 10000, seed=0) | |
rand_row = np.random.randint(dp_mat.shape[0]) | |
rand_col = np.random.randint(dp_mat.shape[1]) | |
dp_mat.set(rand_row, rand_col, 100.678) | |
nc_mat.set(rand_row, rand_col, 100.678) | |
self.assertTrue(cmp_dp_nc_matrix(dp_mat, nc_mat)) | |
def test_set_val1(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -1000, 10000, seed=0) | |
rand_row = np.random.randint(dp_mat.shape[0]) | |
rand_col = np.random.randint(dp_mat.shape[1]) | |
dp_mat.set(rand_row, rand_col, 100) | |
nc_mat.set(rand_row, rand_col, 100) | |
self.assertTrue(cmp_dp_nc_matrix(dp_mat, nc_mat)) | |
def test_set_materror(self): | |
try: | |
dp_mat = [[1, 2, 3], [3, 4, 5]] | |
dp_mat[0, 0, 5] | |
self.assertTrue(False) | |
except TypeError as e: | |
print(e) | |
pass | |
def test_set_args(self): | |
try: | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -1000, 10000, seed=0) | |
dp_mat.set(0, 5) | |
self.assertTrue(False) | |
except TypeError as e: | |
print(e) | |
pass | |
def test_set_rowcolerror(self): | |
try: | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -1000, 10000, seed=0) | |
dp_mat.set(3.4, 34.4, 10) | |
self.assertTrue(False) | |
except TypeError as e: | |
print(e) | |
pass | |
def test_set_rowcolerror1(self): | |
try: | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -1000, 10000, seed=0) | |
dp_mat.set(-5, 0, 4) | |
self.assertTrue(False) | |
except IndexError as e: | |
print(e) | |
pass | |
class TestShape(TestCase): | |
def test_shape(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(2, 2, seed=0) | |
self.assertTrue(dp_mat.shape == nc_mat.shape) | |
def test_shape_mul(self): | |
dp_mat1, nc_mat1 = rand_dp_nc_matrix(1000, 1000, seed=0) | |
dp_mat2, nc_mat2 = rand_dp_nc_matrix(1000, 1000, seed=0) | |
is_correct, speed_up = compute([dp_mat1, dp_mat2], [nc_mat1, nc_mat2], "mul") | |
self.assertTrue(dp_mat1.shape == nc_mat1.shape) | |
self.assertTrue(dp_mat2.shape == nc_mat2.shape) | |
print_speedup(speed_up) | |
def test_shape_pow(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(50, 50, -50, -10, seed=0) | |
is_correct, speed_up = compute([dp_mat, 7], [nc_mat, 7], "pow") | |
self.assertTrue(dp_mat.shape == nc_mat.shape) | |
print_speedup(speed_up) | |
class TestIndexGet(TestCase): | |
def test_index_get(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(2, 2, seed=0) | |
rand_row = np.random.randint(dp_mat.shape[0]) | |
rand_col = np.random.randint(dp_mat.shape[1]) | |
self.assertEqual(round(dp_mat[rand_row][rand_col], decimal_places), | |
round(nc_mat[rand_row][rand_col], decimal_places)) | |
def test_index_get_materror(self): | |
try: | |
dp_mat = [[1, 2, 3], [4, 5, 6]] | |
dp_mat[0, 0] | |
self.assertTrue(False) | |
except TypeError as e: | |
print(e) | |
pass | |
def test_index_get_rowcolerror(self): | |
try: | |
dp_mat, nc_mat = rand_dp_nc_matrix(100, 100, -1000, 10000, seed=0) | |
dp_mat[9.5, 100.3] | |
self.assertTrue(False) | |
except TypeError as e: | |
print(e) | |
pass | |
class TestIndexSet(TestCase): | |
def test_index_set(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(2, 2, seed=0) | |
rand_row = np.random.randint(dp_mat.shape[0]) | |
rand_col = np.random.randint(dp_mat.shape[1]) | |
dp_mat[rand_row][rand_col] = 2 | |
nc_mat[rand_row][rand_col] = 2 | |
self.assertTrue(cmp_dp_nc_matrix(dp_mat, nc_mat)) | |
self.assertEquals(nc_mat[rand_row][rand_col], 2) | |
class TestSlice(TestCase): | |
def test_slice(self): | |
dp_mat, nc_mat = rand_dp_nc_matrix(2, 2, seed=0) | |
self.assertTrue(cmp_dp_nc_matrix(dp_mat[0], nc_mat[0])) | |
self.assertTrue(cmp_dp_nc_matrix(dp_mat[1], nc_mat[1])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment