I'm working on a project which requires me to write my own matrix class implementation. I decided to implement the matrix class as a template to provide compile-time error-checking in the following way:
template<size_t N, size_t M> // N × M matrix
class Matrix
{
// implementation...
};
I managed to implement basic operations such as addition/subtraction, transpose and multiplication. However, I'm having trouble implementing the determinant. I was thinking of implementing it recursively using the Laplace expansion, so I must first implement a way to calculate the i,j minor of a matrix. The problem is, the minor of an N × N matrix is an (N-1) × (N-1) matrix. The following does not compile: (error message is Error C2059 syntax error: '<', pointing to the first line in the function)
template<size_t N>
Matrix<N-1, N-1> Minor(const Matrix<N, N>& mat, size_t i, size_t j)
{
Matrix<N-1, N-1> minor;
// calculate i,j minor
return minor
}
How could I go around this and calculate the minor, while keeping the templated form of the class?
EDIT: I was asked to provide a working example. Here is the relevant part of my code, I tried to keep it as minimal as possible. My Matrix class uses a Vector class, which I also wrote myself. I removed any unrelated code, and also changed any error-checks to asserts, as the actual code throws an exception class, which again was written by me.
Here is the Vector.h file:
#pragma once
#include <vector>
#include <cassert>
template<size_t S>
class Vector
{
public:
Vector(double fInitialValue = 0.0);
Vector(std::initializer_list<double> il);
// indexing range is 0...S-1
double operator[](size_t i) const;
double& operator[](size_t i);
private:
std::vector<double> m_vec;
};
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
template<size_t S>
Vector<S>::Vector(double fInitialValue) : m_vec(S, fInitialValue)
{
}
template<size_t S>
Vector<S>::Vector(std::initializer_list<double> il) : m_vec(il)
{
assert(il.size() == S);
}
template<size_t S>
double Vector<S>::operator[](size_t i) const
{
return m_vec[i];
}
template<size_t S>
double& Vector<S>::operator[](size_t i)
{
return m_vec[i];
}
And here is the Matrix.h file:
#pragma once
#include "Vector.h"
template<size_t N, size_t M>
class Matrix
{
public:
Matrix(double fInitialValue = 0.0);
Matrix(std::initializer_list<Vector<M>> il);
// indexing range is 0...N-1, 0...M-1
Vector<M> operator[](int i) const;
Vector<M>& operator[](int i);
double Determinant() const;
private:
std::vector<Vector<M>> m_mat; // a collection of row vectors
template <size_t N>
friend Matrix<N - 1, N - 1> Minor(const Matrix<N, N>& mat, size_t i, size_t j);
};
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
template<size_t N, size_t M>
Matrix<N, M>::Matrix(double fInitialValue)
: m_mat(N, Vector<M>(fInitialValue)) {}
template<size_t N, size_t M>
Matrix<N, M>::Matrix(std::initializer_list<Vector<M>> il) : m_mat(il)
{
assert(il.size() == N);
}
template<size_t N, size_t M>
Vector<M> Matrix<N, M>::operator[](int i) const
{
return m_mat[i];
}
template<size_t N, size_t M>
Vector<M>& Matrix<N, M>::operator[](int i)
{
return m_mat[i];
}
template<size_t N, size_t M>
double Matrix<N, M>::Determinant() const
{
assert(N == M);
if (N == 2) {
return m_mat[0][0] * m_mat[1][1] - m_mat[0][1] * m_mat[1][0];
}
double det = 0;
for (size_t j = 0; j < N; j++) {
if (j % 2) {
det += m_mat[0][j] * Minor((*this), 0, j).Determinant();
}
else {
det -= m_mat[0][j] * Minor((*this), 0, j).Determinant();
}
}
return det;
}
template <size_t N>
Matrix<N - 1, N - 1> Minor(const Matrix<N, N>& mat, size_t i, size_t j)
{
Matrix<N - 1, N - 1> minor;
for (size_t n = 0; n < i; n++) {
for (size_t m = 0; m < j; m++) {
minor[n][m] = mat[n][m];
}
}
for (size_t n = i + 1; n < N; n++) {
for (size_t m = 0; m < j; m++) {
minor[n - 1][m] = mat[n][m];
}
}
for (size_t n = 0; n < i; n++) {
for (size_t m = j + 1; m < N; m++) {
minor[n][m - 1] = mat[n][m];
}
}
for (size_t n = i + 1; n < N; n++) {
for (size_t m = j + 1; m < N; m++) {
minor[n - 1][m - 1] = mat[n][m];
}
}
return minor;
}
Compiling these along with a simple main.cpp file:
#include "Matrix.h"
#include <iostream>
int main() {
Matrix<3, 3> mat = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
std::cout << mat.Determinant();
}
produces - Error C2760 syntax error: unexpected token '<', expected 'declaration' ...\matrix.h 67
EDIT2: Apparently I had written the template arguments as <N - 1><N - 1> instead of <N -1, N-1> in the implementation of the Minor function. Changing that fixed the error, but introduced a new one - compilation hangs, and after a minute or so I get Error C1060 compiler is out of heap space ...\matrix.h 65
Related
I inspired myself from this link to code a multiplicator of matrix which are multiple of 4: SSE matrix-matrix multiplication
I came up with something somewhat similar, but I observed that if the for loop with j increase by 4 like in the suggest code, it only fill 1 column each 4 column ( which make sense). I can decrease the for loop by 2, and the result is that only half of the column are filled.
So logically, the solution should be to only increase the loop by 1, but when I make the change in the code, I get either segfault error if I use_mm_store_ps or data corrupted size vs. prev_size if I use _mm_storeu_ps, which makes me believe that the data is simply not align.
What and how should I align the data to not cause such error and fill the resulting matrix?
Here is the code I have so far:
void mat_mult(Matrix A, Matrix B, Matrix C, n) {
for(int i = 0; i < n; ++i) {
for(int j = 0; j < n; j+=1) {
__m128 vR = _mm_setzero_ps();
for(int k = 0; k < n; k++) {
__m128 vA = _mm_set1_ps(A(i,k));
__m128 vB = _mm_loadu_ps(&B(k,j));
vR = _mm_add_ss(vR,vA*vB);
}
_mm_storeu_ps(&C(i,j), vR);
}
}
}
I corrected your code, also implemented quite a lot of other supplementary code to fully run tests and print outputs, including that I needed to implement Matrix class from scratch. Following code can be compiled in C++11 standard.
Main corrections to your function are: you should handle separately a case when number of B columns is not multiple of 4, this uneven tail case should be handled by separate loop, you should actually run j loop in steps of 4 (as 128-bit SSE float-32 register contains 4 floats), you should use _mm_mul_ps(vA, vB) instead of vA * vB.
Main bug of your code is that instead of yours _mm_add_ss() you should use _mm_add_ps() because you need to add not single value but 4 of them separately. Only due to usage of _mm_add_ss() you were observed that only 1 out of 4 columns was filled (the rest 3 were zeros).
Alternatively you can fix work of your code by using _mm_load_ss() instead of _mm_loadu_ps() and _mm_store_ss() instead _mm_storeu_ps(). After only this fix your code will give correct result, but will be slow, it will be not faster than regular non-SSE solution. To actually gain speed you have to use only ..._ps() instructions everywhere, also handle correctly case of non-multiple of 4.
Because you don't handle case of B columns being non-multiple of 4, because of this your program segfaults, you just store memory out of bounds of matrix C.
Also you asked a question about alignment. Don't ever use aligned store/load like _mm_store_ps()/_mm_load_ps(), always use _mm_storeu_ps()/_mm_loadu_ps(). Because unaligned access instructions are guaranteed to be of same speed as aligned access instructions for same memory pointers values. But aligned instructions may segfault. So unaligned is always better, same speed and never segfault. It used to be in old time on old CPUs that aligned instructions where faster, but right now they are implemented in CPU with exactly same speed. Aligned instructions don't give any profit, only segfaults. But still you may want to use aligned instructions to intentionally segfault if you want to make sure that your program's memory pointers are always aligned.
I implemented also a separate function with reference slow multiplication of matrices, in order to run a reference test to check the correctness of fast (SSE) multiplication.
As commented out by #АлексейНеудачин, my previous version of Matrix class was allocating unaligned memory for array, now I implemented new helper class AlignmentAllocator which ensures that Matrix is allocating aligned memory, this allocator is used by std::vector<> that stores underlying Matrix's data.
Full code with all the corrections, tests and console outputs plus all the extra supplementary code is below. See also console output after the code, I do print two matrices produced by two different multiplication functions, so that two matrices can be compared visually. All test cases are generated randomly. Scroll down my code a bit to see your fixed function mat_mult(). Also click on Try it online! link if you want to see/run my code online.
Try it online!
#include <cmath>
#include <iostream>
#include <vector>
#include <random>
#include <stdexcept>
#include <string>
#include <iomanip>
#include <cstdlib>
#include <malloc.h>
#include <immintrin.h>
using FloatT = float;
template <typename T, std::size_t N>
class AlignmentAllocator {
public:
typedef T value_type;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T * pointer;
typedef const T * const_pointer;
typedef T & reference;
typedef const T & const_reference;
public:
inline AlignmentAllocator() throw() {}
template <typename T2> inline AlignmentAllocator(const AlignmentAllocator<T2, N> &) throw() {}
inline ~AlignmentAllocator() throw() {}
inline pointer adress(reference r) { return &r; }
inline const_pointer adress(const_reference r) const { return &r; }
inline pointer allocate(size_type n);
inline void deallocate(pointer p, size_type);
inline void construct(pointer p, const value_type & v) { new (p) value_type(v); }
inline void destroy(pointer p) { p->~value_type(); }
inline size_type max_size() const throw() { return size_type(-1) / sizeof(value_type); }
template <typename T2> struct rebind { typedef AlignmentAllocator<T2, N> other; };
bool operator!=(const AlignmentAllocator<T, N> & other) const { return !(*this == other); }
bool operator==(const AlignmentAllocator<T, N> & other) const { return true; }
};
template <typename T, std::size_t N>
inline typename AlignmentAllocator<T, N>::pointer AlignmentAllocator<T, N>::allocate(size_type n) {
#ifdef _MSC_VER
auto p = (pointer)_aligned_malloc(n * sizeof(value_type), N);
#else
auto p = (pointer)aligned_alloc(N, n * sizeof(value_type));
#endif
if (!p)
throw std::bad_alloc();
return p;
}
template <typename T, std::size_t N>
inline void AlignmentAllocator<T, N>::deallocate(pointer p, size_type) {
#ifdef _MSC_VER
_aligned_free(p);
#else
std::free(p);
#endif
}
static size_t constexpr MatrixAlign = 64;
template <typename T, size_t Align = MatrixAlign>
using AlignedVector = std::vector<T, AlignmentAllocator<T, Align>>;
class Matrix {
public:
Matrix(size_t rows, size_t cols)
: rows_(rows), cols_(cols) {
cols_aligned_ = (sizeof(FloatT) * cols_ + MatrixAlign - 1)
/ MatrixAlign * MatrixAlign / sizeof(FloatT);
Clear();
if (size_t(m_.data()) % 64 != 0 ||
(cols_aligned_ * sizeof(FloatT)) % 64 != 0)
throw std::runtime_error("Matrix was allocated unaligned!");
}
Matrix & Clear() {
m_.clear();
m_.resize(rows_ * cols_aligned_);
return *this;
}
FloatT & operator() (size_t i, size_t j) {
if (i >= rows_ || j >= cols_)
throw std::runtime_error("Matrix index (" +
std::to_string(i) + ", " + std::to_string(j) + ") out of bounds (" +
std::to_string(rows_) + ", " + std::to_string(cols_) + ")!");
return m_[i * cols_aligned_ + j];
}
FloatT const & operator() (size_t i, size_t j) const {
return const_cast<Matrix &>(*this)(i, j);
}
size_t Rows() const { return rows_; }
size_t Cols() const { return cols_; }
bool Equal(Matrix const & b, int round = 7) const {
if (Rows() != b.Rows() || Cols() != b.Cols())
return false;
FloatT const eps = std::pow(FloatT(10), -round);
for (size_t i = 0; i < Rows(); ++i)
for (size_t j = 0; j < Cols(); ++j)
if (std::fabs((*this)(i, j) - b(i, j)) > eps)
return false;
return true;
}
private:
size_t rows_ = 0, cols_ = 0, cols_aligned_ = 0;
AlignedVector<FloatT> m_;
};
void mat_print(Matrix const & A, int round = 7, size_t width = 0) {
FloatT const pow10 = std::pow(FloatT(10), round);
for (size_t i = 0; i < A.Rows(); ++i) {
for (size_t j = 0; j < A.Cols(); ++j)
std::cout << std::setprecision(round) << std::fixed << std::setw(width)
<< std::right << (std::round(A(i, j) * pow10) / pow10) << " ";
std::cout << std::endl;;
}
}
void mat_mult(Matrix const & A, Matrix const & B, Matrix & C) {
if (A.Cols() != B.Rows())
throw std::runtime_error("Number of A.Cols and B.Rows don't match!");
if (A.Rows() != C.Rows() || B.Cols() != C.Cols())
throw std::runtime_error("Wrong C rows, cols!");
for (size_t i = 0; i < A.Rows(); ++i)
for (size_t j = 0; j < B.Cols() - B.Cols() % 4; j += 4) {
auto sum = _mm_setzero_ps();
for (size_t k = 0; k < A.Cols(); ++k)
sum = _mm_add_ps(
sum,
_mm_mul_ps(
_mm_set1_ps(A(i, k)),
_mm_loadu_ps(&B(k, j))
)
);
_mm_storeu_ps(&C(i, j), sum);
}
if (B.Cols() % 4 == 0)
return;
for (size_t i = 0; i < A.Rows(); ++i)
for (size_t j = B.Cols() - B.Cols() % 4; j < B.Cols(); ++j) {
FloatT sum = 0;
for (size_t k = 0; k < A.Cols(); ++k)
sum += A(i, k) * B(k, j);
C(i, j) = sum;
}
}
void mat_mult_slow(Matrix const & A, Matrix const & B, Matrix & C) {
if (A.Cols() != B.Rows())
throw std::runtime_error("Number of A.Cols and B.Rows don't match!");
if (A.Rows() != C.Rows() || B.Cols() != C.Cols())
throw std::runtime_error("Wrong C rows, cols!");
for (size_t i = 0; i < A.Rows(); ++i)
for (size_t j = 0; j < B.Cols(); ++j) {
FloatT sum = 0;
for (size_t k = 0; k < A.Cols(); ++k)
sum += A(i, k) * B(k, j);
C(i, j) = sum;
}
}
void mat_fill_random(Matrix & A) {
std::mt19937_64 rng{std::random_device{}()};
std::uniform_real_distribution<FloatT> distr(-9.99, 9.99);
for (size_t i = 0; i < A.Rows(); ++i)
for (size_t j = 0; j < A.Cols(); ++j)
A(i, j) = distr(rng);
}
int main() {
try {
{
Matrix a(17, 23), b(23, 19), c(17, 19), d(c.Rows(), c.Cols());
mat_fill_random(a);
mat_fill_random(b);
mat_mult_slow(a, b, c);
mat_mult(a, b, d);
if (!c.Equal(d, 5))
throw std::runtime_error("Test failed, c != d.");
}
{
Matrix a(3, 7), b(7, 5), c(3, 5), d(c.Rows(), c.Cols());
mat_fill_random(a);
mat_fill_random(b);
mat_mult_slow(a, b, c);
mat_mult(a, b, d);
mat_print(c, 3, 8);
std::cout << std::endl;
mat_print(d, 3, 8);
}
return 0;
} catch (std::exception const & ex) {
std::cout << "Exception: " << ex.what() << std::endl;
return -1;
}
}
Output:
-37.177 -114.438 36.094 -49.689 -139.857
22.113 -127.210 -94.434 -14.363 -6.336
71.878 94.234 33.372 32.573 73.310
-37.177 -114.438 36.094 -49.689 -139.857
22.113 -127.210 -94.434 -14.363 -6.336
71.878 94.234 33.372 32.573 73.310
I have built a class that declares a m, n matrix with elements of different types.
template<typename T>
class Matrix {
public:
int m, n;
T *elements;
How would I now go about overloading an operator to multiply two matrices?
I am mostly confused about dealing with matrices that can take on a variety of sizes.
I know that I will need this line but I am not sure what to do after:
Matrix<T> operator*(Matrix<T> const &b)
The following code is untested, but it should give you an idea of how to do matrix multiplication.
I would suggest defining the operator* as a free function rather than a member function.
template<typename T>
class Matrix
{
public:
Matrix(int r, int c)
: rows(r)
, cols(c)
, elements.resize(rows * cols)
{ }
int rows = 0, cols = 0;
T& operator()(int i, int j) { return elements[i * ncols + j]; }
std::vector<T> elements;
};
template<typename T>
Matrix<T> operator*(Matrix<T> const& a, Matrix<T> const& b)
{
assert(a.cols == b.rows);
Matrix<T> c(a.rows, b.cols);
for (int output_row = 0; output_row < a.rows; ++output_row)
{
for (int output_col = 0; output_col < b.cols; ++output_col)
{
double sum = 0.0;
for (int i = 0; i < a.cols; ++i)
sum += a(output_row, i) * b(i, output_col);
c(output_row, output_col) = sum;
}
}
return c;
}
I am very new to object oriented programming and C++. I have been working on a matrix class and squarematrix class and have been running into some problems that I can't seem to figure out. The error code I have been getting is:
C2784:
'matrix<T,m,k> operator *(matrix<T,m,n> &,matrix<T,n,k> &)': could not
deduce template argument for 'matrix<T,m,n> &' from
'std::vector<std::vector<double,std::allocator<_Ty>>,std::allocator<std::vector<_Ty,std::allocator<_Ty>>>>'
I am really unsure why, because I have had other parts of my code work. The error is reported in the line with "product.elements = product.elements * elements;"
//Source.cpp
#include"Squarematrix.h"
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<double> a = { 1, 2,4,5,6};
squarematrix<double,2> N;
N.assign(a);
cout << N << N.pow(2)<< endl;
return(0);
}
//Matrix.h
#ifndef _Matrix_
#define _Matrix_
#include <iostream>
#include <vector>
#include <math.h>
using namespace std;
template<class T, int m, int n>
class matrix {
public:
vector<vector<T>> elements;
int nrow;
int ncol;
matrix();
matrix(matrix<T, m, n>&);
};
template<class T, int m, int n>
matrix<T, m, n>::matrix() {
vector<T>temp(n, 0);
elements.assign(m, temp);
nrow = m; //m=0
ncol = n; //n=0
}
template<class T, int m, int n>
matrix<T, m, n>::matrix(matrix<T, m, n>& a) {
elements = a.elements;
nrow = m;
ncol = n;
}
template<class T, int m, int n, int k>
matrix<T, m, k> operator*(const matrix<T, m, n>& a, const matrix<T, n, k>& b) {
matrix<T, m, k> product;
for (int i = 0; i < m; i++) {
for (int j = 0; j < k; j++) {
for (int h = 0; h < n; h++)
product.elements[i][j] += a.elements[i][h] * b.elements[h][j];
}
}
return product;
}
template<class T, int m, int n>
ostream& operator<< (ostream& o, const matrix<T, m, n>& input) {
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j)
o << input.elements[i][j] << " ";
o << endl;
}
return o;
}
#endif _Matrix_
//Squarematrix.h
#ifndef _Squarematrix_
#define _Squarematrix_
#include "Matrix.h"
#include <iostream>
#include <vector>
using namespace std;
template<class T, int n>
class squarematrix : public matrix<T, n, n> {
public:
squarematrix();
squarematrix(squarematrix<T, n>&);
squarematrix<T, n> pow(int); //calculate A^k
};
template<class T, int n>
squarematrix<T, n>::squarematrix(){
vector<T>temp(n, 0);
elements.assign(n, temp);
nrow = n; //n=0
ncol = n; //n=0
}
template<class T, int n>
squarematrix<T, n>::squarematrix(squarematrix<T, n>& a){
elements = a.elements;
nrow = n;
ncol = n;
}
template<class T, int n>
squarematrix<T, n> squarematrix<T, n>::pow(int k){
squarematrix<T, n> product;
product.elements = elements;
for (int power = 2; power <= k; power++) {
product.elements = product.elements * elements;
}
return product;
}
#endif _Squarematrix_
You don't need nrow and ncol - they're template parameters and known at compile time.
But that's not the problem - you're multiplying std::vector where you should be multiplying squarematrix:
template<class T, int n>
squarematrix<T, n> squarematrix<T, n>::pow(int k){
squarematrix<T, n> product = unit_matrix<T, n>();
for (int power = 1; power <= k; power++) {
product = product * *this;
}
return product;
}
where I've used a fictitious function that creates a unit matrix.
Writing that function left as an exercise.
this code product.elements = product.elements * elements expresses that you want use two std::vector to multiply, but you don't support the operator * operation with two parameters whose type is std::vector.
In your code, you support a operator * operation with type matrix, so if you want use it, you should change the code product.elements = product.elements * elements to product.elements = (product * *this).elements
that will be OK.
so the code of the member function pow of class Squarematrix is:
template<class T, int n>
squarematrix<T, n> squarematrix<T, n>::pow(int k){
squarematrix<T, n> product;
product.elements = this->elements;
for (int power = 2; power <= k; power++) {
product.elements = (product * *this).elements;
}
return product;
}
at last , #endif is the end of some predefined and don't follow some macro, otherwise, the compiler will throw some warnings or errors.
I am trying to use class templates to matrices. But I have run into a problem with matrix multiplication.
template<typename T, unsigned int N, unsigned int M>
class Matrix : public MatrixBase<Matrix<T, N, M>, T, N, M> {
template<unsigned int K>
friend Matrix<T, N, K> operator*(const Matrix<T, N, M>& m1, const Matrix<T, M, K>& m2) {
Matrix<T, N, K> ret;
for (unsigned int n = 0; n != N; n++) {
for (unsigned int k = 0; k != K; k++) {
ret.i[n][k] = 0;
for (unsigned int m = 0; m != M; m++) {
ret.i[n][k] += m1.i[n][m]*m2.i[m][k];
}
}
}
return ret;
}
};
When it then comes to multiplying two mat4's(4x4 matrices), like so:
m_model = (m_view*m_model);
It gives the error Invalid operands to binary expression ('mat4' (aka 'Matrix<float, 4, 4>') and 'mat4'). Having had a look online I can see this is not the intended use of function templates, as you have to assign on call the template arguments. Is there a way around this similar to what I first intended, i.e. automatic assignment of the template argument based on the second argument of the function?
Here are the definitions of MatrixBase and Matrix(aka mat4) respectively:
MatrixBase
template<typename T , unsigned int M>
struct ComponentColumn{
T& operator[](int m) {
return i[m];
}
const T& operator[](int m) const {
return i[m];
}
T i[M];
};
//-----------MATRIXBASE-----------
template <typename ChildT, typename T, unsigned int N, unsigned int M>
class MatrixBase {
public:
MatrixBase() {}
MatrixBase<ChildT, T, N, M> operator*=(const MatrixBase<ChildT, T, N, M>& m1) {
MatrixBase<ChildT, T, N, M> ret;
for (unsigned int n = 0; n != N; n++) {
for (int k = 0; k != M; k++) {
ret.i[n][k] = 0;
for (unsigned int m = 0; m != M; m++) {
ret.i[n][k] += (*this).i[n][m]*m1.i[m][k];
}
}
}
*this = ret;
return ret;
}
MatrixBase<ChildT, T, N, M> operator+(const MatrixBase<ChildT, T, N, M>& m1) {
MatrixBase<ChildT, T, N, M> ret;
for (int n = 0; n != N; n++) {
for (int m = 0; m != M; m++) {
ret.i[n][m] = i[n][m];
}
}
return ret;
}
ComponentColumn<T, M>& operator[](int n) {
return this->i[n];
}
const ComponentColumn<T, M>& operator[](int n) const {
return this->i[n];
}
explicit operator T*() {
return &(*this)[0][0];
}
protected:
ComponentColumn<T, M> i[N];
};
mat4
template<typename T>
class Matrix<T, 4, 4> : public MatrixBase<Matrix<T, 4, 4>, T, 4, 4> {
public:
Matrix<T, 4, 4>() {
for (unsigned int n = 0; n != 4; n++) {
for (unsigned int m = 0; m != 4; m++) {
if (n == m) {
(*this)[n][m] = 1;
} else {
(*this)[n][m] = 0;
}
}
}
}
Matrix<T, 4, 4>(const Matrix<T, 3, 3>& m) {
(*this)[0][0] = m[0][0]; (*this)[1][0] = m[1][0]; (*this)[2][0] = m[2][0]; (*this)[3][0] = 0;
(*this)[0][1] = m[0][1]; (*this)[1][1] = m[1][1]; (*this)[2][1] = m[2][1]; (*this)[3][1] = 0;
(*this)[0][2] = m[0][2]; (*this)[1][2] = m[1][2]; (*this)[2][2] = m[2][2]; (*this)[3][2] = 0;
(*this)[0][3] = 0; (*this)[1][3] = 0; (*this)[2][3] = 0; (*this)[3][3] = 1;
}
static Matrix<T, 4, 4> Translate(T x, T y, T z);
static Matrix<T, 4, 4> Translate(const vec3& v);
static Matrix<T, 4, 4> Scale(T s);
static Matrix<T, 4, 4> Rotate(T degrees);
static Matrix<T, 4, 4> Frustum(T left, T right, T bottom, T top, T near, T far);
explicit operator Matrix<T, 3, 3>() {
Matrix<T, 3, 3> ret;
for (int n = 0; n != 3; n++) {
for (int m = 0; m != 3; m++) {
ret[n][m] = (*this)[n][m];
}
}
return ret;
}
Matrix<T, 4, 4> Transpose() {
Matrix<T, 4, 4> ret = Matrix<T, 4, 4>();
for (unsigned int n = 0; n != 4; n++) {
for (unsigned int m = 0; m != 4; m++) {
ret.i[n][m] = this->i[m][n];
}
}
*this = ret;
return ret;
}
Matrix<T, 4, 4> Inverse();
};
Unless you are doing this for practice, which would be a good exercise, I would just use an existing linear algebra library which implements matrix vector operations. Such as Armadillo: http://arma.sourceforge.net/
Not an answer, but to share what worked for me and assure the correctness of the method of defining the multiplication operator:
template<typename T, unsigned int N, unsigned int M>
class Matrix {
public:
template<unsigned int K>
friend Matrix<T, N, K> operator*(const Matrix<T, N, M>& m1, const Matrix<T, M, K>& m2) {
Matrix<T, N, K> ret;
for (unsigned int n = 0; n != N; n++) {
for (unsigned int k = 0; k != K; k++) {
ret.i[n][k] = 0;
for (unsigned int m = 0; m != M; m++) {
ret.i[n][k] += m1.i[n][m] * m2.i[m][k];
}
}
}
return ret;
}
array<array<T, M>, N> i;
};
int main() {
Matrix<float, 4, 6> m1; Matrix<float, 6, 10> m2;
auto m3 = (m1 * m2);
cout << m3.i[0][0] << m3.i[3][9] << "\n";
system("pause");
}
In order to learn about C++ templates I am writing a simple Matrix class. So far it has been working well, but I want to add the ability to slice the Matrix to extract a sub-matrix. I am struggling to figure out how to define the size of the return matrix. I have tried the following:
#include <cstdint>
#include <array>
#include <initializer_list>
template<typename T, std::size_t M, std::size_t N>
class Matrix
{
public:
Matrix(void): m_data{0} {}
Matrix(const std::initializer_list<std::initializer_list<T>> m)
{
for(auto i = m.begin(); i != m.end(); i++)
{
for(auto j = i->begin(); j != i->end(); j++)
{
(*this)(i - m.begin(), j - i->begin()) = *j;
}
}
}
T& operator()(const std::size_t i, const std::size_t j)
{
return m_data.at(i + j * N);
}
const T& operator()(const std::size_t i, const std::size_t j) const
{
return m_data.at(i + j * N);
}
template<std::size_t X, std::size_t Y>
Matrix<T,X,Y> slice(const std::size_t iStart, const std::size_t iEnd, const std::size_t jStart, const std::size_t jEnd)
{
Matrix<T,iEnd-iStart+1,jEnd-jStart+1> result;
for(std::size_t i = iStart; i <= iEnd; i++)
{
for(std::size_t j = jStart; j <= jEnd; j++)
{
result(i - iStart, j - jStart) = (*this)(i,j);
}
}
return result;
}
};
int main(void)
{
Matrix<double,3,3> m1 = {{1,2,3},{4,5,6},{7,8,9}};
Matrix<double,2,2> m2 = m1.slice(0,1,0,1);
return 0;
}
But I just get an error saying that 'iEnd' is not a constant expression. What would be the correct way to go about this?
You cannot use function parameters to instantiate templates. You need to pass in the sizes as template arguments to slice:
template<std::size_t iStart, std::size_t iEnd, std::size_t jStart, std::size_t jEnd,
std::size_t I = iEnd-iStart+1, std::size_t J = jEnd-jStart+1>
Matrix<T,I,J> slice()
{
Matrix<T,I,J> result;
///...
return result;
}