Created
January 8, 2014 18:02
-
-
Save charlieda/8321314 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 "sparse.h" | |
void sparse_debug(sparse_matrix* m) | |
{ | |
for(int r = 0; r < m->rows; r++) | |
{ | |
printf("row %d:\n", r); | |
sparse_cell* c = m->row[r]; | |
while(c != NULL) | |
{ | |
printf("\t%d = %d\n", c->col, c->val); | |
c = c->next; | |
} | |
} | |
} | |
/*! | |
@brief constructs a new, blank matrix | |
@param rows the number of row the matrix has | |
@param cols the number of columns the matrix has | |
@returns a pointer to the newly created matrix | |
*/ | |
sparse_matrix* sparse_new(int rows, int cols) | |
{ | |
sparse_matrix* the_matrix = malloc( sizeof(sparse_matrix) ); | |
the_matrix->rows = rows; | |
the_matrix->cols = cols; | |
the_matrix->row = calloc(rows, sizeof(sparse_cell)); | |
return the_matrix; | |
} | |
/*! | |
@brief the destructor for sparse_matrix. Frees all the memory! | |
*/ | |
void sparse_destroy(sparse_matrix* self) | |
{ | |
for(int i = 0; i <self->rows; i++) | |
{ | |
sparse_cell* c = self->row[i]; | |
while(c != NULL) | |
{ | |
sparse_cell* tmp = c; | |
c = c->next; | |
free(tmp); | |
} | |
} | |
free(self); | |
} | |
/*! | |
@brief sets the cell specified by row, col to the value. If the cell does not exist, it is created | |
@param self the matrix to set the value in | |
@param row the row of the cell | |
@param col the column of the cell | |
@param val the value to set the cell to | |
@returns void | |
*/ | |
void sparse_set_cell(sparse_matrix* self, int row, int col, int val) | |
{ | |
if(self->row[row] == NULL) | |
{ | |
// there are no cells in this row yet | |
self->row[row] = malloc(sizeof(sparse_cell)); | |
self->row[row]->col = col; | |
self->row[row]->val = val; | |
self->row[row]->next = NULL; | |
} | |
else | |
{ | |
sparse_cell* c = self->row[row]; | |
while(c->next != NULL && c->col != col) | |
{ | |
c = c->next; | |
} | |
if(c->col == col) | |
{ | |
c->val = val; | |
} | |
else | |
{ | |
if(c->next == NULL) | |
{ | |
// create new cell | |
c->next = malloc(sizeof(sparse_cell)); | |
c->next->col = col; | |
c->next->val = val; | |
c->next->next = NULL; | |
} | |
else | |
{ | |
fprintf(stderr, "sparse_set_cell()\tError: Did not find column value and list end is not NULL\n"); | |
} | |
} | |
} | |
} | |
/*! | |
@brief returns the cell at a specific location in the matrix, or NULL if no such cell exists | |
@param self the matrix to search | |
@param row the row of the value | |
@param col the column of the value | |
@returns the cell at the specified row and column | |
*/ | |
sparse_cell* sparse_cell_at(sparse_matrix* self, int row, int col) | |
{ | |
sparse_cell* c = self->row[row]; | |
while(c != NULL) | |
{ | |
if(c->col == col) | |
{ | |
return c; | |
} | |
c = c->next; | |
} | |
return NULL; | |
} | |
/*! | |
@brief returns the item at a specific location in the matrix | |
@param self the matrix to search | |
@param row the row of the value | |
@param col the column of the value | |
@returns the value of the cell at the specified row and column | |
*/ | |
int sparse_value_at(sparse_matrix* self, int row, int col) | |
{ | |
sparse_cell* c = sparse_cell_at(self, row, col); | |
return c == NULL ? 0 : c->val; | |
} | |
/*! | |
@brief reads the file in and creates a matrix representation | |
@param filename the file to read the matrix from | |
@param transpose if true, switch the rows and columns | |
@returns A pointer to the newly created matrix, or NULL on error | |
*/ | |
sparse_matrix* sparse_read(char* filename, bool transpose) | |
{ | |
char buffer[34]; /* 3 * 10 for the numbers + 2 commas + 2 new line */ | |
// open file | |
FILE* f = fopen(filename, "rt"); | |
if( f == NULL ) | |
{ | |
fprintf(stderr, "sparse_read()\tERROR: could not open input file '%s'\n", filename); | |
return NULL; | |
} | |
// get dimensions | |
int num_rows; | |
int num_cols; | |
if( fgets(buffer, 34, f) != NULL ) | |
{ | |
sscanf(buffer, "%d,%d", &num_rows, &num_cols); | |
} | |
else | |
{ | |
fprintf(stderr, "sparse_read()\tERROR: could not read dimensions of matrix.\n"); | |
} | |
if(transpose) | |
{ | |
int temp = num_rows; | |
num_rows = num_cols; | |
num_cols = temp; | |
} | |
sparse_matrix* the_matrix = sparse_new(num_rows, num_cols); | |
// get values | |
int the_row; | |
int the_col; | |
int the_val; | |
while( !feof(f) ) | |
{ | |
if(fgets(buffer, 34, f) != NULL) | |
{ | |
if(sscanf(buffer, "%d,%d,%d", &the_row, &the_col, &the_val) == 3) | |
{ | |
if(!transpose) | |
{ | |
sparse_set_cell(the_matrix, the_row, the_col, the_val); | |
} | |
else | |
{ | |
sparse_set_cell(the_matrix, the_col, the_row, the_val); | |
} | |
//printf("sparse_read()\tRead (%d, %d, %d)\n", the_row, the_col, the_val); | |
//printf("(90, 25) = %d\n", sparse_value_at(the_matrix, 90, 25)); | |
//sparse_debug(the_matrix); | |
} | |
} | |
} | |
fclose(f); | |
return the_matrix; | |
} | |
/*! | |
@brief writes the matrix to the specified file | |
@param self the matrix to write out | |
@param filename the location of the file to write to | |
@returns true if successful, false otherwise | |
*/ | |
bool sparse_write(sparse_matrix* self, char* filename) | |
{ | |
FILE* f = fopen(filename, "wt"); | |
if(filename == NULL) | |
{ | |
f = stdout; | |
} | |
if(f == NULL) | |
{ | |
fprintf(stderr, "sparse_write()\tFailed to open file '%s' for writing.\n", filename); | |
return false; | |
} | |
//-=-=-=-=-= | |
fprintf(f, "%d,%d\n", self->rows, self->cols); | |
for(int r = 0; r < self->rows; r++) | |
{ | |
for(int c = 0; c < self->cols; c++) | |
{ | |
if(sparse_value_at(self, r, c) != 0) | |
{ | |
fprintf(f, "%d,%d,%d\n", r, c, sparse_value_at(self, r, c)); | |
} | |
} | |
} | |
fclose(f); | |
return true; | |
} | |
/*! | |
@brief transposes the matrix i.e. interchanges the rows and columns | |
@param self the matrix to transpose | |
@returns a new matrix, which is the transposed version of self. | |
*/ | |
sparse_matrix* sparse_transposed(sparse_matrix* self) | |
{ | |
sparse_matrix* to_return = sparse_new(self->cols, self->rows); | |
for(int r = 0; r < self->rows; r++) | |
{ | |
for(int c = 0; c < self->cols; c++) | |
{ | |
int val = sparse_value_at(self, r, c); | |
if(val != 0) | |
{ | |
//printf("sparse_transposed() - value at %d %d is non-zero (%d)!\n", r, c, val); | |
sparse_set_cell(to_return, c, r, val); | |
} | |
} | |
} | |
return to_return; | |
} | |
/*! | |
@brief adds two matrices together, into a new matrix. For a more efficient function, see sparse_add | |
@param a the first matrix | |
@param b the second matrix | |
@returns a pointer to a new matrix, which is the value of a * b, or NULL if the matrices are not compatible | |
*/ | |
sparse_matrix* sparse_sum(sparse_matrix* a, sparse_matrix* b) | |
{ | |
if(a->rows != b->rows || a->cols != b->cols) | |
{ | |
fprintf(stderr, "sparse_sum() - dimensions not compatible."); | |
return NULL; | |
} | |
sparse_matrix* to_return = sparse_new(a->rows, a->cols); | |
for(int r = 0; r < a->rows; r++) | |
{ | |
for(int c = 0; c < a->cols; c++) | |
{ | |
int val = sparse_value_at(a, r, c) + sparse_value_at(b, r, c); | |
if(val != 0) sparse_set_cell(to_return, r, c, val); | |
} | |
} | |
return to_return; | |
} | |
/*! | |
@brief adds the values of matrix b to matrix a. This is more efficient than sparse_sum. | |
@param a the matrix to add the values too | |
@param b the matrix to get the second set of values from | |
@returns matrix a. | |
*/ | |
sparse_matrix* sparse_add(sparse_matrix* a, sparse_matrix* b) | |
{ | |
if(a->rows != b->rows || a->cols != b->cols) | |
{ | |
fprintf(stderr, "sparse_sum_() - dimensions not compatible."); | |
return NULL; | |
} | |
// start with the non-zero cells in matrix a | |
// matching cells in b are marked by negating the column | |
for(int r = 0; r < a->rows; r++) | |
{ | |
sparse_cell* cell_a = a->row[r]; | |
while(cell_a != NULL) | |
{ | |
sparse_cell* cell_b = sparse_cell_at(b, r, cell_a->col); | |
if(cell_b != NULL) | |
{ | |
cell_a->val += cell_b->val; | |
cell_b->col *= -1; // negate column to mark that this cell has been dealt with | |
} | |
cell_a = cell_a->next; | |
} | |
} | |
// deal with the non-zero cells in matrix b. | |
// cells with a positive column value are added into matrix a. | |
// cells with a negative column value are "fixed", by making the column positive again. | |
for(int r = 0; r < b->rows; r++) | |
{ | |
sparse_cell* cell = b->row[r]; | |
while(cell != NULL) | |
{ | |
if(cell->col >= 0) | |
{ | |
//printf("sparse_add()\t\tat (%d, %d) - setting to %d\n", r, cell->col, cell->val); | |
sparse_set_cell(a, r, cell->col, cell->val); | |
} | |
else | |
{ | |
cell->col *= -1; // fix matrix b | |
} | |
cell = cell->next; | |
} | |
} | |
return a; | |
} | |
/*! | |
@brief multiplies two matrices together | |
@param a the first matrix | |
@param b the second matrix | |
@returns a pointer to a new matrix, which is the result of a . b, or NULL if the matrices are not compatible | |
*/ | |
sparse_matrix* sparse_mul(sparse_matrix* a, sparse_matrix* b) | |
{ | |
if(a->cols != b->rows) { fprintf(stderr, "sparse_mul() - ERROR: Matrices do not have compatible dimensions\n"); return NULL; } | |
sparse_matrix* ab = sparse_new(a->rows, b->cols); | |
for(int i = 0; i < a->rows; i++) | |
{ | |
for(int j = 0; j < b->cols; j++) | |
{ | |
int sum = 0; | |
for(int k = 0; k < a->cols; k++) | |
{ | |
sum += sparse_value_at(a, i, k) * sparse_value_at(b, k, j); | |
} | |
sparse_set_cell(ab, i, j, sum); | |
} | |
} | |
return ab; | |
} | |
/*! | |
@brief multiplies two matrices together | |
@param a the first matrix | |
@param b the second matrix. This should be transposed before being passed here | |
@returns a pointer to a new matrix, which is the result of a . b, or NULL if the matrices are not compatible | |
@warning matrix b should be transposed before being passed to this function | |
*/ | |
sparse_matrix* sparse_mul_optimised(sparse_matrix* a, sparse_matrix* b) | |
{ | |
if(a->cols != b->cols) { fprintf(stderr, "sparse_mul_optimised() - ERROR: Matrices do not have compatible dimensions\n"); return NULL; } | |
sparse_matrix* ab = sparse_new(a->rows, b->rows); | |
for(int i = 0; i < a->rows; i++) | |
{ | |
for(int j = 0; j < b->rows; j++) | |
{ | |
int sum = 0; | |
sparse_cell* cell_a = a->row[i]; | |
while(cell_a != NULL) | |
{ | |
sum += cell_a->val * sparse_value_at(b, j, cell_a->col); | |
cell_a = cell_a->next; | |
} | |
// if(cell_a != NULL && cell_b != NULL) | |
// { | |
// for(int k = 0; k < a->cols; k++) | |
// { | |
// sum += sparse_value_at(a, i, k) * sparse_value_at(b, j, k); | |
// } | |
// } | |
sparse_set_cell(ab, i, j, sum); | |
} | |
} | |
return ab; | |
} | |
/*! | |
@brief compares two matrices | |
@param a the first matrix | |
@param b the second matrix | |
@returns true if matricies are equal, false otherwise | |
*/ | |
bool sparse_is_equal(sparse_matrix* a, sparse_matrix* b) | |
{ | |
if(a == NULL || b == NULL) return false; | |
if(a->rows != b->rows) return false; | |
if(a->cols != b->cols) return false; | |
if(a == b) return true; | |
bool valid = true; | |
for(int r = 0; r < a->rows; r++) | |
{ | |
for(int c = 0; c < a->cols; c++) | |
{ | |
if(sparse_value_at(a, r, c) != sparse_value_at(b, r, c)) | |
{ | |
fprintf(stderr, "sparse_is_equal() - Failed at (%d, %d): %d != %d\n", r, c, sparse_value_at(a, r, c), sparse_value_at(b, r, c)); | |
valid = false; // it would be more efficient to return false here, but to aid debugging we continue | |
} | |
} | |
} | |
return valid; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment