how to implement matrix multiplication in C? - c

I was trying to create an algorithm to perform matrix multiplication.
I've designed matrix as follows:
// matrix.h
#pragma once
#include <stdlib.h>
#include <string.h>
struct matrix {
size_t rows, cols;
double* data;
};
extern struct matrix* mat_mul(const struct matrix* m1, const struct matrix* m2);
// matrix.c
#include "matrix.h"
void mat_constr(struct matrix* m, size_t rows, size_t cols) {
m->rows = rows; m->cols = cols;
m->data = calloc(rows * cols, sizeof(double));
if (!m->data) {
return;
}
}
void mat_destr(struct matrix* m) {
free(m->data);
}
mat_constr is matrix constructor, and mat_destr is mat_destructor. To test the algorithm I've used this main
// main
int main(void) {
struct matrix A;
mat_constr(&A, 2, 3);
memcpy(A.data, (double[6]) { 1, 2, 3, 4, 5, 6 }, 6 * sizeof(double));
struct matrix B;
mat_constr(&B, 3, 2);
memcpy(B.data, (double[6]) { 7, 8, 9, 10, 11, 12 }, 6 * sizeof(double));
struct matrix* C = mat_mul(&A, &B);
mat_destr(&A); mat_destr(&B);
mat_destr(C);
return 0;
}
and this is the mat_mul function
struct matrix* mat_mul(const struct matrix* m1, const struct matrix* m2) {
if ((m1 == NULL) || (m2 == NULL)) {
return NULL;
}
if (m1->cols != m2->rows) {
return NULL;
}
struct matrix* result = malloc(sizeof(struct matrix));
if (!result) {
return NULL;
}
mat_constr(result, m1->rows, m2->cols);
size_t k = 1;
for (size_t r = 0; r < m1->rows; r++) {
for (size_t c = 0; c < m1->cols; c++) {
result->data[r * result->cols + c] = m1->data[r * m1->cols + k] * m2->data[k * m2->cols + c];
}
k++;
}
return result;
}
in order to perform matrix multiplication, I have to use this sum: sum from k = 1 to m1->cols of a_i k-th column * a_j k-th row (in this forum I don't know how to write using mathjax, because symbols like this $$ $$ doesn't work here).
this is the minimal reproducible example:
// matrix.h
#pragma once
#include <stdlib.h>
#include <string.h>
struct matrix {
size_t rows, cols;
double* data;
};
extern struct matrix* mat_mul(const struct matrix* m1, const struct matrix* m2);
// matrix.c
#include "matrix.h"
void mat_constr(struct matrix* m, size_t rows, size_t cols) {
m->rows = rows; m->cols = cols;
m->data = calloc(rows * cols, sizeof(double));
if (!m->data) {
return;
}
}
void mat_destr(struct matrix* m) {
free(m->data);
}
struct matrix* mat_mul(const struct matrix* m1, const struct matrix* m2) {
if ((m1 == NULL) || (m2 == NULL)) {
return NULL;
}
if (m1->cols != m2->rows) {
return NULL;
}
struct matrix* result = malloc(sizeof(struct matrix));
if (!result) {
return NULL;
}
mat_constr(result, m1->rows, m2->cols);
size_t k = 1;
for (size_t r = 0; r < m1->rows; r++) {
for (size_t c = 0; c < m1->cols; c++) {
result->data[r * result->cols + c] = m1->data[r * m1->cols + k] * m2->data[k * m2->cols + c];
}
k++;
}
return result;
}
int main(void) {
struct matrix A;
mat_constr(&A, 2, 3);
memcpy(A.data, (double[6]) { 1, 2, 3, 4, 5, 6 }, 6 * sizeof(double));
struct matrix B;
mat_constr(&B, 3, 2);
memcpy(B.data, (double[6]) { 7, 8, 9, 10, 11, 12 }, 6 * sizeof(double));
struct matrix* C = mat_mul(&A, &B);
mat_destr(&A); mat_destr(&B);
mat_destr(C);
return 0;
}
this solution allocates enough memory, and return the pointer of the new allocated matrix correctly. But the problem is in the last for-loops. According to my linear algebra knowledge, I have to scroll columns by columns the first matrix, and scroll rows by rows the second matrix. But these for-loops are not correct.
I have only one question: "why is this method of computing matrix multiplication wrong? how can I solve it?"
note that r * cols + c gives exactly the index of the i-th entry of the matrix.

You are simply missing a nested for loop and a +=; The correct code for multiplying your 2 matrices would be something like:
for (size_t r = 0; r < m1->rows; ++r)
for (size_t c = 0; c < m2->cols; ++c)
for (size_t k = 0; k < m2->rows; ++k)
result->data[r * result->cols + c] += m1->data[r * m1->cols + k] * m2->data[k * m2->cols + c]

Related

Printing a matrix causes a segmentation fault

I tried to print a matrix based on this code but somehow it causes a segmentation fault.
I want to transfer the given arrays to the struct member data and then print them out by referecing to data.
I scanned through it but I cannot find my mistake. Maybe I messed up when allocating memory for the structure.
#include "base.h"
struct Matrix {
int rows; // number of rows
int cols; // number of columns
double** data; // a pointer to an array of n_rows pointers to rows; a row is an array of n_cols doubles
};
typedef struct Matrix Matrix;
Matrix* make_matrix(int n_rows, int n_cols) {
Matrix* mat = xcalloc(n_rows,sizeof(double*));
mat->rows = n_rows;
mat->cols = n_cols;
return mat;
}
Matrix* copy_matrix(double* data, int n_rows, int n_cols) {
Matrix* mat = make_matrix(n_rows, n_cols);
for(int i = 0; i < (mat->rows); i++)
{
for(int j = 0; j < (mat->cols); j++)
{
mat->data[i][j] = data[i] + j;
}
}
return mat;
}
void print_matrix(Matrix* m)
{
for(int i = 0; i < (m->rows); i++)
{
for(int j = 0; j < (m->cols); j++)
{
printf("%g", m->data[i][j]);
}
}
}
void matrix_test(void) {
double a[] = {
1, 2, 3,
4, 5, 6,
7, 8, 9 };
Matrix* m1 = copy_matrix(a, 3, 3);
print_matrix(m1);
double a2[] = {
1, 2, 3,
4, 5, 6 };
Matrix* m2 = copy_matrix(a2, 2, 3);
print_matrix(m2);
double a3[] = {
1, 2,
3, 4,
5, 6 };
Matrix* m3 = copy_matrix(a3, 3, 2);
print_matrix(m3);
}
In this particular example, there is no obvious reason why you should allocate the struct itself dynamically, since it's just 3 items. Because this is plain wrong:
Matrix* mat = xcalloc(n_rows,sizeof(double*));
Either you have to allocate room for the struct and its pointer array separately, or you can just skip dynamic allocation of the struct and then only allocate room for the data member.
Furthermore xcalloc(n_rows,sizeof(double*)); only allocates room for an array of pointers, not what those pointers point at. So you need to allocate room for each data item too, in a separate loop.
Furthermore, using a pointer array for a matrix is almost certainly needlessly obscure and inefficient practice. Instead you should use a 2D array as shown here: Correctly allocating multi-dimensional arrays
You should also free() all memory that you allocated.
Here is a cleaned up example where I replaced the pointer to pointer with a so-called "flexible array member". This has the advantage of allocating the struct together with the data for faster access. Also all the problems with fragmented allocation will be gone.
The down side is that a flexible array member has to to be regarded as a flat 1D array, so we have to cast to a pointer to 2D array type before using it with array[i][j] syntax.
#include <stdio.h>
#include <stdlib.h>
typedef struct {
size_t rows;
size_t cols;
double data[];
} Matrix;
Matrix* make_matrix (size_t n_rows, size_t n_cols) {
Matrix* mat = malloc(sizeof(Matrix) + sizeof(double[n_rows][n_cols]));
if(mat == NULL)
{
return NULL;
}
mat->rows = n_rows;
mat->cols = n_cols;
return mat;
}
Matrix* copy_matrix (const double* data, size_t n_rows, size_t n_cols) {
Matrix* mat = make_matrix(n_rows, n_cols);
double (*new_data)[mat->cols];
new_data = (double(*)[mat->cols]) mat->data;
for(size_t i = 0; i < mat->rows; i++)
{
for(size_t j = 0; j < mat->cols; j++)
{
new_data[i][j] = data[i] + j;
}
}
return mat;
}
void print_matrix (const Matrix* mat)
{
double (*data)[mat->cols];
data = (double(*)[mat->cols]) mat->data;
for(size_t i = 0; i < mat->rows; i++)
{
for(size_t j = 0; j < mat->cols; j++)
{
printf("%g ", data[i][j]);
}
printf("\n");
}
}
int main (void) {
double a[] = {
1, 2, 3,
4, 5, 6,
7, 8, 9 };
Matrix* m1 = copy_matrix(a, 3, 3);
print_matrix(m1);
puts("");
double a2[] = {
1, 2, 3,
4, 5, 6 };
Matrix* m2 = copy_matrix(a2, 2, 3);
print_matrix(m2);
puts("");
double a3[] = {
1, 2,
3, 4,
5, 6 };
Matrix* m3 = copy_matrix(a3, 3, 2);
print_matrix(m3);
free(m1);
free(m2);
free(m3);
}

local variable may point to deallocated memory

i have a struct in the header (ex3.h)
typedef struct matrix {
double **data;
size_t n_rows;
size_t n_columns;
} matrix;
in the c file i have function "create_matrix" and another function slice
and i'm getting warning "local variable may point to deallocated memory" in some places.. i mark them as "--->" in comments
#include <stdlib.h>
# include <stdio.h>
# include "ex3.h"
#define DEBUG
matrix *create_matrix (size_t n_rows, size_t n_columns)
{
matrix *mat = malloc (sizeof (matrix));
if (!mat)
{
fprintf (stderr, POINTER_ERROR);
return NULL;
}
if (n_rows == ZERO || n_columns == ZERO)
{ mat->data = NULL, mat->n_rows = ZERO, mat->n_columns = ZERO; }
else
{
{
double **mat_data = malloc (n_rows * sizeof (double *));
if (!mat_data)
{
free (mat);
fprintf (stderr, POINTER_ERROR);
return NULL;
}
for (int i = ZERO; i < n_rows; i++)
{
mat_data[i] = malloc (n_columns * sizeof (double));
if (!mat_data[i])
{
for (int k = ZERO; k < i; k++)
{
free (mat_data[k]);
}
free (mat_data), free (mat);
fprintf (stderr, POINTER_ERROR);
return NULL;
}
else
{
for (int j = ZERO; j < n_columns; j++)
{
mat_data[i][j] = (double) ZERO;
}
}
}
mat->data = mat_data, mat->n_rows = n_rows, mat->n_columns = n_columns;
}
}
return mat;
}
matrix *slice (matrix *mat, size_t row_start,
size_t row_end, size_t col_start, size_t col_end)
{
"some condions..."
{
matrix *m = create_matrix (ZERO, ZERO); //nul matrix
if (!m) "<---"
{
return NULL;
}
return m; "<---"
}
else
{
size_t row = row_end - row_start, col = col_end - col_start;
matrix *new_mat = create_matrix (row, col);
if (!new_mat)"<---"
{
return NULL;
}
for (int i = ZERO; i < row; i++)
{
for (int j = ZERO; j < col; j++)
{
"--->" (new_mat->data)[i][j] = mat->data[i + row_start][j + col_start];
}
}
return new_mat; "<---"
}
}
i made a few conditions that insure i'm not try to go to a space in the memory place that is not part of my matrix, so why clion still make a warning
thanks for trying to help!
I would do it another way. Instead plenty of allocations I would use only one. Instead of negative tests (ie is something is wrong) I would use positive tests and have only one return point from the function.
typedef struct matrix {
size_t n_rows;
size_t n_columns;
double data[];
} matrix;
matrix *allocate(size_t r, size_t c)
{
matrix *m = malloc(sizeof(*m) + r * c * sizeof(m -> data[0]));
if(m)
{
m -> n_rows = r;
m -> n_columns = c;
}
return m;
}
matrix *slice(matrix *m, size_t sr, size_t er, size_t sc, size_t ec)
{
matrix *result = NULL;
size_t lc = ec - sc, lr = er - sr;
if(m && m -> n_rows > er && m -> n_rows > sr && sr <= er && m -> n_columns > ec && m -> n_columns > sc && sc <= ec)
{
result = allocate(lr, lc);
if(result)
{
double (*dest)[lc] = &result -> data;
double (*src)[m -> n_columns] = &m -> data;
result -> n_rows = lr;
result -> n_columns = lc;
for(size_t row = 0; row < lr; row ++)
{
memcpy(dest[row], &src[sr + row][sc], lc * sizeof(m -> data[0]));
}
}
}
return result;
}

C multithreading slower than single-threading when multiplying matrices

I'm using theads in my C code to make the code faster, but it actually makes it worse.
I have a matrix and a matrix_operation class :
struct matrix
{
char *name;
size_t rows;
size_t columns;
double *value;
};
typedef struct matrix_operation matrix_operation;
struct matrix_operation
{
matrix r;
matrix m1;
matrix m2;
size_t row;
};
To multiply the matrices, I use these functions :
matrix matrix_mul(char *name, matrix m1, matrix m2, size_t replace)
{
matrix r = matrix_init(name, m1.rows, m2.columns);
matrix_operation *mat = malloc(sizeof *mat * m1.rows);
pthread_t *th = malloc(sizeof *th * m1.rows);
for (size_t i = 0; i < m1.rows; i++)
{
matrix_operation param = {r, m1, m2, i};
mat[i] = param;
pthread_create(&th[i], NULL , matrix_mul_th, &mat[i]);
}
for (size_t i = 0; i < m1.rows; i++)
{
pthread_join(th[i], NULL);
}
free(mat);
free(th);
if (replace == 1)
matrix_free(m1);
else if (replace == 2)
matrix_free(m2);
else if (replace == 3)
{
matrix_free(m1);
matrix_free(m2);
}
return r;
}
void *matrix_mul_th(void *arg)
{
matrix_operation mat = *(matrix_operation*)arg;
for (size_t j = 0; j < mat.m2.columns; j++)
{
double sum = 0;
for (size_t k = 0; k < mat.m1.columns; k++)
sum += matrix_get(mat.m1,mat.row,k) * matrix_get(mat.m2,k,j);
matrix_put(mat.r,mat.row,j,sum);
}
return NULL;
}
Do you have any idea why the problem may be ? And how to improve the code ?
The matrices are stored as a 1D array.
Thanks a lot for your time,
Lucas

How do I assign values to a matrix inside a struct?

I have a header file with this type definition:
typedef struct {
int rows;
int columns;
int **values;
} bidimensional_matrix;
As an example, If I instantiate the matrix from a main function I'd just do:
int matrix[][] = {{1, 2, 3}, {1, 1, 1}, {5, 5, 5}};
How would you generate the same matrix but with the typedef provided previously? (I mean, with pointers and malloc)
Is the approach correct? Maybe I'm a little object oriented biased and in c it's not convenient to handle it that way. I've defined the struct that way so I can just pass two bidimensional_matrix by parameter and do a multiplication.
I propose you to use flexible member array, exemple:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
size_t n;
size_t m;
int matrix[];
} bidimensional_matrix;
bidimensional_matrix *new_bidimensional_matrix(size_t n, size_t m) {
bidimensional_matrix *bm = malloc(sizeof *bm + sizeof *bm->matrix * n * m);
if (!bm) {
return NULL;
}
*bm = (bidimensional_matrix){ .n = n, .m = m };
return bm;
}
int get_bidimensional_matrix(bidimensional_matrix *bm, size_t i, size_t j) {
return bm->matrix[i * bm->m + j];
}
int set_bidimensional_matrix(bidimensional_matrix *bm, size_t i, size_t j, int x) {
return bm->matrix[i * bm->m + j] = x;
}
int main(void) {
bidimensional_matrix *bm = new_bidimensional_matrix(5, 10);
if (!bm) {
return EXIT_FAILURE;
}
for (size_t i = 0; i < bm->n * bm->m; i++) {
bm->matrix[i] = i;
}
printf("sample value %d\n", get_bidimensional_matrix(bm, 4, 5));
set_bidimensional_matrix(bm, 4, 5, 42);
printf("sample value %d\n", get_bidimensional_matrix(bm, 4, 5));
free(bm);
}
But you could use this too, that have other avantage but generally is more slow:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
size_t n;
size_t m;
int **matrix;
} bidimensional_matrix;
int main(void) {
bidimensional_matrix bm = { .n = 5, .m = 10, .matrix = malloc(sizeof *bm.matrix * bm.n) };
if (!bm.matrix) {
return EXIT_FAILURE;
}
for (size_t i = 0; i < bm.n; i++) {
bm.matrix[i] = malloc(sizeof *bm.matrix[i] * bm.m);
if (!bm.matrix[i]) {
return EXIT_FAILURE;
}
for (size_t j = 0; j < bm.m; j++) {
bm.matrix[i][j] = i * bm.m + j;
}
}
printf("sample value %d\n", bm.matrix[4][5]);
for (size_t i = 0; i < bm.n; i++) {
free(bm.matrix[i]);
}
free(bm.matrix);
}
If you need to swap rows the second could be a little faster cause swap row are O(1). But like you see the first one has only one malloc(), in practice with the cache of the processor it should be a lot more faster than the second implementation.

Direct sum of matrix function

This struct allows representing arbitrary size matrices, where M is the number of rows, N is the number of columns and data is a pointer to M*N values of type double stored by rows.
struct matrix {
size_t M, N;
double *data;
};
struct matrix *mat_directsum(const struct matrix *a, const struct matrix *b);
The function mat_directsum accepts two pointers to arrays as a parameter and should return to the direct sum, dynamically allocated on the heap.
Example:
A.M = 2
A.N = 3
A.data = (1, 1, 2, 0, 1, -3)
Example of direct sum function
I just need a few tips on how to set the function, just to see how others work with arrays of this type, because the only way that comes to mind is an iterative methods with many loops, however, it is enough work long and ingenious, I would like to know if there are easier method to solve it. Thank you
ps.
(memory allocation is not a problem of course)
EDIT
I solved like that:
struct matrix *mat_directsum(const struct matrix *a, const struct matrix *b) {
struct matrix *c = malloc(sizeof(struct matrix));
c->M = a->M + b->M;
c->N = a->N + b->N;
int n = c->M * c->M;
double *dati = calloc(n, sizeof(double));
int t = 0;//index new array
int y = 0;//index first mat
int z = 0;//index second mat
for (int i = 0; i < c->N; i++) {
if (i < a->N) {//first mat
for (int j = 0; j < c->M; j++) {
if (j < a->M) {
dati[t] = a->data[y];
y++;
}
t++;
}
} else {//second mat
for (int j = 0; j < c->M; j++) {
if (j >= a->M) {
dati[t] = b->data[z];
z++;
}
t++;
}
}
}
c->data = dati;
return c;
}
I don't know how to do it which only one for loop
//macro which will point to an element indexed at [xe][ye]
#define ELEMENT(data,rows,columns,xe,ye) (data+((xe)*(columns)+(ye)))
struct matrix
{
size_t M, N;
double *data;
};
//won't mind changing the return type from "struct matrix*" to "struct matrix"
struct matrix mat_directsum(const struct matrix *a, const struct matrix *b)
{
int x;
struct matrix res;
res.M = a->M + b->M;
res.N = a->N + b->N;
//using calloc will set the memory to zero i.e all the bytes will be set to zero.
res.data = (double*)calloc(res.M * res.N, sizeof(double));
if(res.data == NULL)
{
return res;
}
for(x = 0; x < a->M; ++x)
{
memcpy(ELEMENT(res.data, res.M, res.N, x, 0), ELEMENT(a->data, a->M, a->N, x, 0), a->N * sizeof(double));
}
for(x = 0; x < b->M; ++x)
{
//note the offset by [a->M][a->N] while accessing elements of res.
memcpy(ELEMENT(res.data, res.M, res.N, x + a->M, a->N), ELEMENT(b->data, b->M, b->N, x, 0), b->N * sizeof(double));
}
return res;
}
struct matrix res = mat_directsum(&a, &b);
if(res.data != NULL)
{
free(res.data);
}
Besides the error n = c->M * c->M, spotted by M.M (what a coincidence of Ms!), your solution has another error in the for loops: You confused the row and column numbers M and N - since the values are stored by rows, the outer loop has to be for (int i = 0; i < c->M; i++) and the inner loops have to be for (int j = 0; j < c->N; j++), so all M and N in those loops (also in the ifs) have to be swapped. Apart from that and the missing allocation error checks, your solution is fine.
I don't know how to do it which only one for loop
If you want to see an alternative approach, here's one with a helper function to insert the matrices into the sum matrix:
#include <string.h>
void mat_insert(const struct matrix *s, struct matrix *d, int r, int c)
{ // copy source matrix s to destination matrix d at row r, column c
for (int i = 0; i < s->M; i++) // for each row
memcpy(d->data+(r+i)*d->N+c, s->data+i*s->N, s->N*sizeof*s->data);
}
struct matrix *mat_directsum(const struct matrix *a, const struct matrix *b)
{
struct matrix *c = malloc(sizeof *c); if (!c) return NULL;
c->M = a->M + b->M;
c->N = a->N + b->N;
int n = c->M * c->N;
c->data = calloc(n, sizeof *c->data); if (!c->data) return free(c), NULL;
mat_insert(a, c, 0, 0); // copy a to c at row 0, column 0
mat_insert(b, c, a->M, a->N); // copy b to c at row a->M, column a->N
return c;
}

Resources