/** * @file Matrix.hpp * * A simple matrix template library. * * @author James Goppert */ #pragma once #include #include #if defined(SUPPORT_STDIOSTREAM) #include #include #endif // defined(SUPPORT_STDIOSTREAM) #include "math.hpp" // There is a bug in GCC 4.8, which causes the compiler to segfault due to array {} constructors. // Do for-loop constructors just for GCC 4.8 #ifdef __GNUC__ #define MATRIX_GCC_4_8_WORKAROUND (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9)) #else #define MATRIX_GCC_4_8_WORKAROUND 0 #endif namespace matrix { template class Vector; template class Matrix; template class Slice; template class Matrix { #if MATRIX_GCC_4_8_WORKAROUND Type _data[M][N]; #else Type _data[M][N] {}; #endif public: // Constructors #if MATRIX_GCC_4_8_WORKAROUND Matrix() { for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { _data[i][j] = Type{}; } } } #else Matrix() = default; #endif explicit Matrix(const Type data_[M*N]) { memcpy(_data, data_, sizeof(_data)); } explicit Matrix(const Type data_[M][N]) { memcpy(_data, data_, sizeof(_data)); } Matrix(const Matrix &other) { memcpy(_data, other._data, sizeof(_data)); } template Matrix(const Slice& in_slice) { Matrix& self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { self(i, j) = in_slice(i, j); } } } /** * Accessors/ Assignment etc. */ inline Type operator()(size_t i, size_t j) const { assert(i >= 0); assert(i < M); assert(j >= 0); assert(j < N); return _data[i][j]; } inline Type &operator()(size_t i, size_t j) { assert(i >= 0); assert(i < M); assert(j >= 0); assert(j < N); return _data[i][j]; } Matrix & operator=(const Matrix &other) { if (this != &other) { memcpy(_data, other._data, sizeof(_data)); } return (*this); } void copyTo(Type dst[M*N]) const { memcpy(dst, _data, sizeof(Type)*M*N); } void copyToColumnMajor(Type dst[M*N]) const { const Matrix &self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { dst[i+(j*M)] = self(i, j); } } } /** * Matrix Operations */ // this might use a lot of programming memory // since it instantiates a class for every // required mult pair, but it provides // compile time size_t checking template Matrix operator*(const Matrix &other) const { const Matrix &self = *this; Matrix res; res.setZero(); for (size_t i = 0; i < M; i++) { for (size_t k = 0; k < P; k++) { for (size_t j = 0; j < N; j++) { res(i, k) += self(i, j) * other(j, k); } } } return res; } Matrix emult(const Matrix &other) const { Matrix res; const Matrix &self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { res(i, j) = self(i, j)*other(i, j); } } return res; } Matrix edivide(const Matrix &other) const { Matrix res; const Matrix &self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { res(i, j) = self(i, j)/other(i, j); } } return res; } Matrix operator+(const Matrix &other) const { Matrix res; const Matrix &self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { res(i, j) = self(i, j) + other(i, j); } } return res; } Matrix operator-(const Matrix &other) const { Matrix res; const Matrix &self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { res(i, j) = self(i, j) - other(i, j); } } return res; } // unary minus Matrix operator-() const { Matrix res; const Matrix &self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { res(i, j) = -self(i, j); } } return res; } void operator+=(const Matrix &other) { Matrix &self = *this; self = self + other; } void operator-=(const Matrix &other) { Matrix &self = *this; self = self - other; } template void operator*=(const Matrix &other) { Matrix &self = *this; self = self * other; } /** * Scalar Operations */ Matrix operator*(Type scalar) const { Matrix res; const Matrix &self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { res(i, j) = self(i, j) * scalar; } } return res; } inline Matrix operator/(Type scalar) const { return (*this)*(1/scalar); } Matrix operator+(Type scalar) const { Matrix res; const Matrix &self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { res(i, j) = self(i, j) + scalar; } } return res; } inline Matrix operator-(Type scalar) const { return (*this) + (-1*scalar); } void operator*=(Type scalar) { Matrix &self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { self(i, j) = self(i, j) * scalar; } } } void operator/=(Type scalar) { Matrix &self = *this; self = self * (Type(1) / scalar); } inline void operator+=(Type scalar) { *this = (*this) + scalar; } inline void operator-=(Type scalar) { *this = (*this) - scalar; } bool operator==(const Matrix &other) const { return isEqual(*this, other); } bool operator!=(const Matrix &other) const { const Matrix &self = *this; return !(self == other); } /** * Misc. Functions */ void write_string(char * buf, size_t n) const { buf[0] = '\0'; // make an empty string to begin with (we need the '\0' for strlen to work) const Matrix &self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { snprintf(buf + strlen(buf), n - strlen(buf), "\t%8.8g", double(self(i, j))); // directly append to the string buffer } snprintf(buf + strlen(buf), n - strlen(buf), "\n"); } } void print(FILE *stream = stdout) const { // element: tab, point, 8 digits; row: newline; string: \0 end static const size_t n = 10*N*M + M + 1; char * buf = new char[n]; write_string(buf, n); fprintf(stream, "%s\n", buf); delete[] buf; } Matrix transpose() const { Matrix res; const Matrix &self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { res(j, i) = self(i, j); } } return res; } // tranpose alias inline Matrix T() const { return transpose(); } template const Slice slice(size_t x0, size_t y0) const { return Slice(x0, y0, this); } template Slice slice(size_t x0, size_t y0) { return Slice(x0, y0, this); } const Slice row(size_t i) const { return slice<1, N>(i,0); } Slice row(size_t i) { return slice<1, N>(i,0); } const Slice col(size_t j) const { return slice(0,j); } Slice col(size_t j) { return slice(0,j); } void setRow(size_t i, const Matrix &row_in) { slice<1,N>(i,0) = row_in.transpose(); } void setRow(size_t i, Type val) { slice<1,N>(i,0) = val; } void setCol(size_t j, const Matrix &column) { slice(0,j) = column; } void setCol(size_t j, Type val) { slice(0,j) = val; } void setZero() { memset(_data, 0, sizeof(_data)); } inline void zero() { setZero(); } void setAll(Type val) { Matrix &self = *this; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { self(i, j) = val; } } } inline void setOne() { setAll(1); } inline void setNaN() { setAll(NAN); } void setIdentity() { setZero(); Matrix &self = *this; for (size_t i = 0; i < M && i < N; i++) { self(i, i) = 1; } } inline void identity() { setIdentity(); } inline void swapRows(size_t a, size_t b) { assert(a >= 0); assert(a < M); assert(b >= 0); assert(b < M); if (a == b) { return; } Matrix &self = *this; for (size_t j = 0; j < N; j++) { Type tmp = self(a, j); self(a, j) = self(b, j); self(b, j) = tmp; } } inline void swapCols(size_t a, size_t b) { assert(a >= 0); assert(a < N); assert(b >= 0); assert(b < N); if (a == b) { return; } Matrix &self = *this; for (size_t i = 0; i < M; i++) { Type tmp = self(i, a); self(i, a) = self(i, b); self(i, b) = tmp; } } Matrix abs() const { Matrix r; for (size_t i=0; i max_val) { max_val = val; } } } return max_val; } Type min() const { Type min_val = (*this)(0,0); for (size_t i=0; i &self = *this; bool result = true; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { result = result && isnan(self(i, j)); } } return result; } }; template Matrix zeros() { Matrix m; m.setZero(); return m; } template Matrix ones() { Matrix m; m.setOne(); return m; } template Matrix nans() { Matrix m; m.setNaN(); return m; } template Matrix operator*(Type scalar, const Matrix &other) { return other * scalar; } template bool isEqual(const Matrix &x, const Matrix &y, const Type eps=1e-4f) { for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { if (!isEqualF(x(i,j), y(i,j), eps)) { return false; } } } return true; } namespace typeFunction { template Type min(const Type x, const Type y) { bool x_is_nan = isnan(x); bool y_is_nan = isnan(y); // z > nan for z != nan is required by C the standard if (x_is_nan || y_is_nan) { if (x_is_nan && !y_is_nan) { return y; } if (!x_is_nan && y_is_nan) { return x; } return x; } return (x < y) ? x : y; } template Type max(const Type x, const Type y) { bool x_is_nan = isnan(x); bool y_is_nan = isnan(y); // z > nan for z != nan is required by C the standard if (x_is_nan || y_is_nan) { if (x_is_nan && !y_is_nan) { return y; } if (!x_is_nan && y_is_nan) { return x; } return x; } return (x > y) ? x : y; } template Type constrain(const Type x, const Type lower_bound, const Type upper_bound) { if (lower_bound > upper_bound) { return NAN; } else if(isnan(x)) { return NAN; } else { return typeFunction::max(lower_bound, typeFunction::min(upper_bound, x)); } } } template Matrix min(const Matrix &x, const Type scalar_upper_bound) { Matrix m; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { m(i,j) = typeFunction::min(x(i,j),scalar_upper_bound); } } return m; } template Matrix min(const Type scalar_upper_bound, const Matrix &x) { return min(x, scalar_upper_bound); } template Matrix min(const Matrix &x1, const Matrix &x2) { Matrix m; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { m(i,j) = typeFunction::min(x1(i,j),x2(i,j)); } } return m; } template Matrix max(const Matrix &x, const Type scalar_lower_bound) { Matrix m; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { m(i,j) = typeFunction::max(x(i,j),scalar_lower_bound); } } return m; } template Matrix max(const Type scalar_lower_bound, const Matrix &x) { return max(x, scalar_lower_bound); } template Matrix max(const Matrix &x1, const Matrix &x2) { Matrix m; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { m(i,j) = typeFunction::max(x1(i,j),x2(i,j)); } } return m; } template Matrix constrain(const Matrix &x, const Type scalar_lower_bound, const Type scalar_upper_bound) { Matrix m; if (scalar_lower_bound > scalar_upper_bound) { m.setNaN(); } else { for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { m(i,j) = typeFunction::constrain(x(i,j), scalar_lower_bound, scalar_upper_bound); } } } return m; } template Matrix constrain(const Matrix &x, const Matrix &x_lower_bound, const Matrix &x_upper_bound) { Matrix m; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { m(i,j) = typeFunction::constrain(x(i,j), x_lower_bound(i,j), x_upper_bound(i,j)); } } return m; } #if defined(SUPPORT_STDIOSTREAM) template std::ostream& operator<<(std::ostream& os, const matrix::Matrix& matrix) { for (size_t i = 0; i < M; ++i) { os << "["; for (size_t j = 0; j < N; ++j) { os << std::setw(10) << matrix(i, j); os << "\t"; } os << "]" << std::endl; } return os; } #endif // defined(SUPPORT_STDIOSTREAM) } // namespace matrix /* vim: set et fenc=utf-8 ff=unix sts=0 sw=4 ts=4 : */