Background
Some time ago I've encountered some very good articles about neural networks that represented an ANN as a set of matrices, so everything was done using matrix operations. These articles show code written in Python, and since I know Python well, I decided to translate it to C++ (which I'm currently learning). Very soon I understood that it's too difficult to represent a matrix as a vector of vectors and mess with many of these vectors. That's why I decided to write my own library to do maths with matrices. Now it's a project on GitHub called Matrix.
Matrix
I know there are some libraries to work with matrices out there, like BLAS, Eigen, etc. Matrix was originally written for educational purpose, but now I'm also aiming for speed.
You can do almost any mathematical operation with an object of type Matrix
as described in the Matrix Wiki.
Internally, all the data is stored in a vector
of vector
s of double
. My attempts to make Matrix
a template
class were unsuccessful.
Code
This is the code responsible for matrix-by-matrix multiplication
Matrix Matrix::operator*(const Matrix& right) const {
if (cols != right.rows) {
std::string msg=std::string("Size mismatch while multiplying matrices: ").append(to_string(rows).append(std::string("X")).append(to_string(cols)));
msg.append(std::string(" vs ").append(to_string(right.rows)).append(std::string("X")).append(to_string(right.cols)));
throw SizeException(msg);
}
if (right.IsNum())
return this->operator*(right.M[0][0]);
size_t a, b, c;
Matrix res(rows, right.cols);
if (right.IsCol()) {
for (a = 0; a < cols; ++a)
res.M[0][0] += M[0][a] * right.M[a][0];
return res;
} else if (this->IsSquare(2) && right.IsSquare(2)) {
// loop unrolling for 2x2 matrices
res.M[0][0] = M[0][0] * right.M[0][0] + M[0][1] * right.M[1][0],
res.M[0][1] = M[0][0] * right.M[0][1] + M[0][1] * right.M[1][1],
res.M[1][0] = M[1][0] * right.M[0][0] + M[1][1] * right.M[1][0],
res.M[1][1] = M[1][0] * right.M[0][1] + M[1][1] * right.M[1][1];
return res;
}
for (a = 0; a < rows; ++a) {
for (b = 0; b < right.cols; ++b) {
double tmp;
for (c = 0, tmp = 0; c < cols; ++c) tmp += M[a][c] * right.M[c][b];
res.M[a][b] = tmp;
}
}
return res;
}
Here M
is a vector containing all the data of a matrix, IsNum
and IsCol
determine whether a matrix contains only one number and whether it consists of only one column.
This code provides operator[]
to get a row of a matrix or its single number.
Matrix& Matrix::operator[](const long i) const {
if (i < 0 || i == rows)
throw SizeException("Index out of range");
static Matrix ret;
if (rows != 1) {
ret.Reshape(1, cols);
long a;
for (a = 0; a < cols; ++a) ret.M[0][a] = this->M[i][a];
} else {
ret.Reshape(1, 1);
ret.M[0][0] = this->M[0][i];
}
return ret;
}
Reshape(long rows, long cols)
changes the size of a matrix to (rows, columns)
. I'm wondering whether this method could be made more effective.
This is a constructor that accepts two arguments: number of rows and columns in a matrix.
Matrix::Matrix(long rows, long cols) {
long a;
this->rows = rows, this->cols = cols;
this->M.resize(rows);
for (a = 0; a < rows; ++a) this->M[a].resize(cols);
this->prettified=false;
}
The problem here is that the profiler shows that a lot of time is spent on resizing the M
vector. Is it possible to avoid this?
Questions
I'm very new to C++, so my code could look like messed up garbage (although I've put lots of effort to make it look beautiful). What can be done to make my style better?
Compared to Python's NumPy, my matrix multiplication is quite a bit slower. Can it be made faster without using sophisticated matrix multiplication algorithms like Strassen's?
What else could be done to make Matrix more efficient?
Note:
I'm unable to copy and paste the whole code here since it's pretty huge, so please refer to Matrix GitHub repo.
append()
s). C++ has string formatting, right…? \$\endgroup\$snprintf
and that's all. \$\endgroup\$snprintf()
is from the C standard library. I don't know much about C++, but I thought you weren't supposed to use C stuff like that. \$\endgroup\$