## More on Vectors and Matrices

[subheading]Integration of other (array style) Vector data[/subheading]

So going back to my earlier implementation of a Vector class I'd like to add a way to interact with other implementations and "raw vectors" (also known as array). Luckily everything is already based around expression templates which are easily extendable. What I want is a way to interpret raw data as a Vector compatible with my implementation. All I have to do for that is copy & paste my whole Vector class, change its name to "InPlaceVector", replace "T data[D];" with "T *const data;" and provide a constructor that takes a pointer. Then I add the following function for convenience

[source lang="cpp"]

template

InPlaceVector MakeVector(T *const p)

{

return InPlaceVector(p);

}

[/source]

and can apply the full functionality of my Vector class to any kind of other vector class or representation that stores its data in an array.

Example:

[source lang="cpp"]

double raw_vector[3];

Vector a(1,2,3);

MakeVector(raw_vector) += a;

[/source]

And since the InPlaceVector object only contains a const pointer, it will once again very likely be optimized away and we don't even incur any conversion overhead.

So with that problem out of the way (sort of). We can move on to matrices.

[subheading]The Matrix[/subheading]

So if we are already in need of vectors chance are we also want matrices. A simple Matrix class can be obtained by just copy pasting my Vector class, replace the "operator[](unsigned)" with "operator()(unsigned, unsigned)" etc. When implementing matrices one also has to decide between row and column major order for storage. I went with column major since that makes the matrices directly usable with OpenGL. An argument for row major order would be that the native c++ matrices (double mat[3][3]) are row major and are therefore more natural in C++.

Besides the element wise operations we "inherited" from the vector class we also need matrix-vector multiplication and matrix-matrix multiplication. These could be implemented directly, but to make it more elegant we will first introduce a way to access the rows and columns like they were vector expressions. I guess this can be considered a form of the decorator pattern:

[source lang="cpp"]

template

class RowVectorExpr : public VectorExpr > {

public:

RowVectorExpr(const A& pa, const unsigned& i) : a(pa), index(i) { }

inline T operator[](unsigned i) const { return a(index, i); }

private:

const A& a;

const unsigned index;

};

template

inline RowVectorExpr

Row(const MatrixExpr &a, const unsigned &index)

{

return RowVectorExpr(a, index);

}

//similarly for columns

[/source]

There are other interesting "operations" that can and should also be implemented like this like submatrix access and transposition.

Now with easy acess to rows and columns we can write the matrix-matrix multiplication as:

[source lang=cpp"]

template

class MatMatMulExpr : public MatrixExpr > {

public:

MatMatMulExpr(const A& pa, const B& pb) : a(pa), b(pb) { }

inline T operator()(unsigned i, unsigned j) const { return dot(Row(a,i),Column(b,j)); }

private:

const A& a;

const B& b;

};

template

inline MatMatMulExpr

operator*(const MatrixExpr &a, const MatrixExpr &b)

{

return MatMatMulExpr(a, b);

}

//similarly for matrix-vector and vector-matrix multiplications

[/source]

[subheading]Expression templates and pitfalls[/subheading]

So this is the point where after all the praise for expression templates I have to address a problem with them. Expression templates offer a form of lazy evaluation. This means that operations are not carried out immediately. Instead they are carried out when the result is actually needed. This has advantages when not the whole calculation is actually required. Using the tools we just introduced consider for example this:

[source lang="cpp"]

Matrix A, B;

//fill A and B with some values

Vector v = Row(A*B,2); //save the third row of A*B in v

[/source]

In the assignment to v only the matrix elements that lie in the requested Row are actually calculated!

The downside of this is that the elements will be calculated multiple times if they are requested multiple times. This can happen when more than two matrices are multiplied in a single expression. In that case the temporary objects we avoided with the expression templates might actually be beneficial from a performance perspective. Even worse, operations where the left operand of an assignment is also part of the expression will even produce wrong results.

[source lang="cpp"]

Matrix A;

A = A*Transpose(A); //gives wrong result!

[/source]

For pure Vector operations this usually isn't a problem given that the dependencies are only component wise, but for matrix and matrix-vector multiplications this has to be taken into account.

An easy way to solve both of these problems is to use an explicit evaluation function:

[source lang="cpp"]

template

inline Vector eval(const VectorExpr& a)

{

return Vector(a);

}

template

inline Matrix eval(const MatrixExpr& a)

{

return Matrix(a);

}

[/source]

This allows us to force the execution of an expression and get a temporary from it.

[source lang="cpp"]

Matrix A,B,C,D;

A = eval(A*Transpose(A)); //correct!

A = eval(B*C)*D//less operations in exchange for a temporary object!

[/source]

It would also be possible to have the expression templates detect these situations by passing "source" references through the expression tree and some template specialization magic. At the moment I'll stick with the "eval" solution.

Here is the extended Vector header and the Matrix header I'm using right now:

Vector.h

[source lang="cpp"]

/*

* MathVector.h - Copyright (C) 2011 Jakob Progsch

*

* This software is provided 'as-is', without any express or implied

* warranty. In no event will the authors be held liable for any damages

* arising from the use of this software.

*

* Permission is granted to anyone to use this software for any purpose,

* including commercial applications, and to alter it and redistribute it

* freely, subject to the following restrictions:

*

* 1. The origin of this software must not be misrepresented; you must not

* claim that you wrote the original software. If you use this software

* in a product, an acknowledgment in the product documentation would be

* appreciated but is not required.

*

* 2. Altered source versions must be plainly marked as such, and must not be

* misrepresented as being the original software.

*

* 3. This notice may not be removed or altered from any source

* distribution.

*/

/*

* Vector.h provides a simple static vector template class with

* a basic expression template ansatz for vector operations.

*/

#ifndef VECTOR_H

#define VECTOR_H

#include

#include

#include

template class Vector;

//base class for all expression templates

template

class VectorExpr {

public:

inline operator const A&() const

{

return *static_cast(this);

}

};

//better use macros instead of copy pasting this stuff all over the place

#define MAKE_VEC_VEC_EXPRESSION(NAME, EXPR, FUNCTION) \

template \

class NAME : public VectorExpr > { \

public: \

NAME(const A& pa, const B& pb) : a(pa), b(pb) { } \

inline T operator[](unsigned i) const { return EXPR; } \

private: \

const A& a; \

const B& b; \

}; \

\

template \

inline NAME \

FUNCTION(const VectorExpr &a, const VectorExpr &b) \

{ \

return NAME(a, b); \

}

#define MAKE_VEC_SCAL_EXPRESSION(NAME, EXPR, FUNCTION) \

template \

class NAME : public VectorExpr > { \

public: \

NAME(const A& pa, const T& pb) : a(pa), b(pb) { } \

inline T operator[](unsigned i) const { return EXPR; } \

private: \

const A& a; \

const T& b; \

}; \

\

template \

inline NAME \

FUNCTION(const VectorExpr &a, const T &b) \

{ \

return NAME(a, b); \

}

#define MAKE_SCAL_VEC_EXPRESSION(NAME, EXPR, FUNCTION) \

template \

class NAME : public VectorExpr > { \

public: \

NAME(const T& pa, const A& pb) : a(pa), b(pb) { } \

inline T operator[](unsigned i) const { return EXPR; } \

private: \

const T& a; \

const A& b; \

}; \

\

template \

inline NAME \

FUNCTION(const T &a, const VectorExpr &b) \

{ \

return NAME(a, b); \

}

#define MAKE_VEC_EXPRESSION(NAME, EXPR, FUNCTION) \

template \

class NAME : public VectorExpr > { \

public: \

NAME(const A& pa) : a(pa) { } \

inline T operator[](unsigned i) const { return EXPR; } \

private: \

const A& a; \

}; \

\

template \

inline NAME \

FUNCTION(const VectorExpr &a) \

{ \

return NAME(a); \

}

//create actual functions and operators

MAKE_VEC_VEC_EXPRESSION (EMulExpr, a * b, multiply_elements)

MAKE_VEC_VEC_EXPRESSION (EDivExpr, a / b, divide_elements)

MAKE_VEC_VEC_EXPRESSION (AddExpr, a + b, operator+)

MAKE_VEC_VEC_EXPRESSION (SubExpr, a - b, operator-)

MAKE_VEC_SCAL_EXPRESSION(DivExpr, a / b, operator/)

MAKE_VEC_SCAL_EXPRESSION(MulExpr1, a * b, operator*)

MAKE_SCAL_VEC_EXPRESSION(MulExpr2, a * b, operator*)

MAKE_VEC_EXPRESSION (NegExpr, -a, operator-)

//sub vector proxy

template

class SubVectorExpr : public VectorExpr > {

public:

SubVectorExpr(const A& pa, const unsigned& o)

: a(pa), offset(o) { }

inline T operator[](unsigned i) const

{ return a[i+offset]; }

private:

const A& a;

const unsigned offset;

};

template

inline SubVectorExpr

SubVector(const VectorExpr &a, const unsigned &o)

{

return SubVectorExpr(a, o);

}

//static size assertion since the vector size ist also static

template

struct STATIC_DIMENSION_MISMATCH_ASSERTION;

template

struct STATIC_DIMENSION_MISMATCH_ASSERTION { };

#define ASSERT_DIMENSION(I, J) sizeof(STATIC_DIMENSION_MISMATCH_ASSERTION)

//actual vector class

template

class Vector : public VectorExpr > {

public:

static const unsigned Dim = D;

typedef T Type;

Vector()

{

std::fill(data, data+Dim, T());

}

Vector(const T &p1)

{

ASSERT_DIMENSION(1, Dim);

data[0] = p1;

}

Vector(const T &p1, const T &p2)

{

ASSERT_DIMENSION(2, Dim);

data[0] = p1;

data[1] = p2;

}

Vector(const T &p1, const T &p2, const T &p3)

{

ASSERT_DIMENSION(3, Dim);

data[0] = p1;

data[1] = p2;

data[2] = p3;

}

Vector(const T &p1, const T &p2, const T &p3, const T &p4)

{

ASSERT_DIMENSION(4, Dim);

data[0] = p1;

data[1] = p2;

data[2] = p3;

data[3] = p4;

}

T* raw() { return data; }

template

Vector(const VectorExpr& a)

{

const A& ao ( a );

for(unsigned i = 0;i data = ao;

}

T& operator[] (unsigned i) { return data; }

const T& operator[] (unsigned i) const { return data; }

const Vector& operator*=(const T &b)

{

for(unsigned i = 0;i data *= b;

return *this;

}

const Vector& operator/=(const T &b)

{

for(unsigned i = 0;i data /= b;

return *this;

}

template

const Vector& operator+=(const VectorExpr& a)

{

const A& ao ( a );

for(unsigned i = 0;i data += ao;

return *this;

}

template

const Vector& operator-=(const VectorExpr& a)

{

const A& ao ( a );

for(unsigned i = 0;i data -= ao;

return *this;

}

template

Vector& operator=(const VectorExpr& a)

{

const A& ao ( a );

for(unsigned i = 0;i data = ao;

return *this;

}

Vector& normalize()

{ *this /= abs(*this); return *this; }

private:

T data[Dim];

};

//InPlaceVector to use raw data

template

class InPlaceVector : public VectorExpr > {

public:

static const unsigned Dim = D;

typedef T Type;

InPlaceVector(T *const d) : data(d)

{ }

template

InPlaceVector(const VectorExpr& a)

{

const A& ao ( a );

for(unsigned i = 0;i data = ao;

}

T* raw() { return data; }

T& operator[] (unsigned i) { return data; }

const T& operator[] (unsigned i) const { return data; }

const InPlaceVector& operator*=(const T &b)

{

for(unsigned i = 0;i data *= b;

return *this;

}

const InPlaceVector& operator/=(const T &b)

{

for(unsigned i = 0;i data /= b;

return *this;

}

template

const InPlaceVector& operator+=(const VectorExpr& a)

{

const A& ao ( a );

for(unsigned i = 0;i data += ao;

return *this;

}

template

const InPlaceVector& operator-=(const VectorExpr& a)

{

const A& ao ( a );

for(unsigned i = 0;i data -= ao;

return *this;

}

template

InPlaceVector& operator=(const VectorExpr& a)

{

const A& ao ( a );

for(unsigned i = 0;i data = ao;

return *this;

}

InPlaceVector& normalize()

{ *this /= abs(*this); return *this; }

private:

T *const data;

};

template

InPlaceVector MakeVector(T *const p)

{

return InPlaceVector(p);

}

template

inline Vector eval(const VectorExpr& a)

{

return Vector(a);

}

//"reduction" functions that don't return expression templates

template

inline T sum(const VectorExpr& a)

{

const A& ao ( a );

T res = 0;

for(unsigned i = 0;i res += ao;

return res;

}

template

inline T dot(const VectorExpr& a, const VectorExpr& b)

{

return sum(multiply_elements(a, b));

}

template

inline T squared_norm(const VectorExpr& a)

{

const A& ao ( a );

T res = 0;

for(unsigned i = 0;i {

T tmp = ao;

res += tmp*tmp;

}

return res;

}

template

inline T abs(const VectorExpr& a)

{

return std::sqrt(squared_norm(a));

}

template

std::ostream& operator& a)

{

const A& ao ( a );

out for(unsigned i = 1;i {

out ;

}

out

return out;

}

#undef MAKE_VEC_VEC_EXPRESSION

#undef MAKE_VEC_SCAL_EXPRESSION

#undef MAKE_SCAL_VEC_EXPRESSION

#undef MAKE_VEC_EXPRESSION

#endif

[/source]

Matrix.h

[source lang="cpp"]

/*

* MathMatrix.h - Copyright (C) 2011 Jakob Progsch

*

* This software is provided 'as-is', without any express or implied

* warranty. In no event will the authors be held liable for any damages

* arising from the use of this software.

*

* Permission is granted to anyone to use this software for any purpose,

* including commercial applications, and to alter it and redistribute it

* freely, subject to the following restrictions:

*

* 1. The origin of this software must not be misrepresented; you must not

* claim that you wrote the original software. If you use this software

* in a product, an acknowledgment in the product documentation would be

* appreciated but is not required.

*

* 2. Altered source versions must be plainly marked as such, and must not be

* misrepresented as being the original software.

*

* 3. This notice may not be removed or altered from any source

* distribution.

*/

/*

* Matrix.h provides a simple static Matrix template class with

* a basic expression template ansatz for Matrix operations.

*/

#ifndef Matrix_H

#define Matrix_H

#include "Vector.h"

template class Matrix;

//base class for all expression templates

template

class MatrixExpr {

public:

inline operator const A&() const

{

return *static_cast(this);

}

};

//better use macros instead of copy pasting this stuff all over the place

#define MAKE_MAT_MAT_EXPRESSION(NAME, EXPR, FUNCTION) \

template \

class NAME : public MatrixExpr > { \

public: \

NAME(const A& pa, const B& pb) : a(pa), b(pb) { } \

inline T operator()(unsigned i, unsigned j) const { return EXPR; } \

private: \

const A& a; \

const B& b; \

}; \

\

template \

inline NAME \

FUNCTION(const MatrixExpr &a, const MatrixExpr &b)\

{ \

return NAME(a, b); \

}

#define MAKE_MAT_SCAL_EXPRESSION(NAME, EXPR, FUNCTION) \

template \

class NAME : public MatrixExpr > { \

public: \

NAME(const A& pa, const T& pb) : a(pa), b(pb) { } \

inline T operator()(unsigned i, unsigned j) const { return EXPR; } \

private: \

const A& a; \

const T& b; \

}; \

\

template \

inline NAME \

FUNCTION(const MatrixExpr &a, const T &b) \

{ \

return NAME(a, b); \

}

#define MAKE_SCAL_MAT_EXPRESSION(NAME, EXPR, FUNCTION) \

template \

class NAME : public MatrixExpr > { \

public: \

NAME(const T& pa, const A& pb) : a(pa), b(pb) { } \

inline T operator()(unsigned i, unsigned j) const { return EXPR; } \

private: \

const T& a; \

const A& b; \

}; \

\

template \

inline NAME \

FUNCTION(const T &a, const MatrixExpr &b) \

{ \

return NAME(a, b); \

}

#define MAKE_MAT_EXPRESSION(NAME, EXPR, FUNCTION) \

template \

class NAME : public MatrixExpr > { \

public: \

NAME(const A& pa) : a(pa) { } \

inline T operator()(unsigned i, unsigned j) const { return EXPR; } \

private: \

const A& a; \

}; \

\

template \

inline NAME \

FUNCTION(const MatrixExpr &a) \

{ \

return NAME(a); \

}

//create actual functions and operators

MAKE_MAT_MAT_EXPRESSION (MATEMulExpr, a(i,j) * b(i,j), multiply_elements)

MAKE_MAT_MAT_EXPRESSION (MATEDivExpr, a(i,j) / b(i,j), divide_elements)

MAKE_MAT_MAT_EXPRESSION (MATAddExpr, a(i,j) + b(i,j), operator+)

MAKE_MAT_MAT_EXPRESSION (MATSubExpr, a(i,j) - b(i,j), operator-)

MAKE_MAT_SCAL_EXPRESSION(MATDivExpr, a(i,j) / b, operator/)

MAKE_MAT_SCAL_EXPRESSION(MATMulExpr1, a(i,j) * b, operator*)

MAKE_SCAL_MAT_EXPRESSION(MATMulExpr2, a * b(i,j), operator*)

MAKE_MAT_EXPRESSION (MATNegExpr, -a(i,j), operator-)

//transposition

template

class TransExpr : public MatrixExpr > {

public:

TransExpr(const A& pa) : a(pa) { }

inline T operator()(unsigned i, unsigned j) const { return a(j,i); }

private:

const A& a;

};

template

inline TransExpr

Transpose(const MatrixExpr &a)

{

return TransExpr(a);

}

//Row, Column vector and submatrix proxies

template

class RowVectorExpr : public VectorExpr > {

public:

RowVectorExpr(const A& pa, const unsigned& i) : a(pa), index(i) { }

inline T operator[](unsigned i) const { return a(index, i); }

private:

const A& a;

const unsigned index;

};

template

inline RowVectorExpr

Row(const MatrixExpr &a, const unsigned &index)

{

return RowVectorExpr(a, index);

}

template

class ColumnVectorExpr : public VectorExpr > {

public:

ColumnVectorExpr(const A& pa, const unsigned& i) : a(pa), index(i) { }

inline T operator[](unsigned i) const { return a(i, index); }

private:

const A& a;

const unsigned index;

};

template

inline ColumnVectorExpr

Column(const MatrixExpr &a, const unsigned &index)

{

return ColumnVectorExpr(a, index);

}

template

class SubMatrixExpr : public MatrixExpr > {

public:

SubMatrixExpr(const A& pa, const unsigned& i, const unsigned& j)

: a(pa), offseti(i), offsetj(j) { }

inline T operator()(unsigned i, unsigned j) const

{ return a(i+offseti, j+offsetj); }

private:

const A& a;

const unsigned offseti, offsetj;

};

template

inline SubMatrixExpr

SubMatrix(const MatrixExpr &a, const unsigned &i, const unsigned &j)

{

return SubMatrixExpr(a, i, j);

}

//matrix-vector and vector-matrix multiplications

template

class MatVecMulExpr : public VectorExpr > {

public:

MatVecMulExpr(const A& pa, const B& pb) : a(pa), b(pb) { }

inline T operator[](unsigned i) const { return dot(Row(a, i), b); }

private:

const A& a;

const B& b;

};

template

inline MatVecMulExpr

operator*(const MatrixExpr &a, const VectorExpr &b)

{

return MatVecMulExpr(a, b);

}

template

class VecMatMulExpr : public VectorExpr > {

public:

VecMatMulExpr(const A& pa, const B& pb) : a(pa), b(pb) { }

inline T operator[](unsigned i) const { return dot(Column(a, i), b); }

private:

const A& a;

const B& b;

};

template

inline VecMatMulExpr

operator*(const VectorExpr &b, const MatrixExpr &a)

{

return VecMatMulExpr(a, b);

}

template

class MatMatMulExpr : public MatrixExpr > {

public:

MatMatMulExpr(const A& pa, const B& pb) : a(pa), b(pb) { }

inline T operator()(unsigned i, unsigned j) const { return dot(Row(a,i),Column(b,j)); }

private:

const A& a;

const B& b;

};

template

inline MatMatMulExpr

operator*(const MatrixExpr &a, const MatrixExpr &b)

{

return MatMatMulExpr(a, b);

}

template

class Identity : public MatrixExpr > {

public:

inline T operator()(unsigned i, unsigned j) const { return i==j?1:0; }

};

//actual Matrix class

template

class Matrix : public MatrixExpr > {

public:

static const unsigned Dim1 = D1;

static const unsigned Dim2 = D2;

typedef T Type;

Matrix()

{

std::fill(data, data+Dim1*Dim2, T());

}

template

Matrix(const MatrixExpr& a)

{

const A& ao ( a );

for(unsigned j = 0;j for(unsigned i = 0;i operator()(i,j) = ao(i,j);

}

T* raw() { return data; }

T& operator() (unsigned i, unsigned j) { return data[i+j*Dim1]; }

const T& operator() (unsigned i, unsigned j) const { return data[i+j*Dim1]; }

const Matrix& operator*=(const T &b)

{

for(unsigned i = 0;i data *= b;

return *this;

}

const Matrix& operator/=(const T &b)

{

for(unsigned i = 0;i data /= b;

return *this;

}

template

const Matrix& operator+=(const MatrixExpr& a)

{

const A& ao ( a );

for(unsigned j = 0;j for(unsigned i = 0;i operator()(i,j) += ao(i,j);

return *this;

}

template

const Matrix& operator-=(const MatrixExpr& a)

{

const A& ao ( a );

for(unsigned j = 0;j for(unsigned i = 0;i operator()(i,j) -= ao(i,j);

return *this;

}

template

const Matrix& operator*=(const MatrixExpr& a)

{

const A& ao ( a );

*this = eval(*this * a);

return *this;

}

template

Matrix& operator=(const MatrixExpr& a)

{

const A& ao ( a );

for(unsigned j = 0;j for(unsigned i = 0;i operator()(i,j) = ao(i,j);

return *this;

}

private:

T data[Dim1*Dim2];

};

//InPlaceMatrix to use raw data COLUMN MAJOR!

template

class InPlaceMatrix : public MatrixExpr > {

public:

static const unsigned Dim1 = D1;

static const unsigned Dim2 = D2;

typedef T Type;

InPlaceMatrix(T *const d) : data(d)

{ }

template

InPlaceMatrix(const MatrixExpr& a)

{

const A& ao ( a );

for(unsigned j = 0;j for(unsigned i = 0;i operator()(i,j) = ao(i,j);

}

T* raw() { return data; }

T& operator() (unsigned i, unsigned j) { return data[i+j*Dim1]; }

const T& operator() (unsigned i, unsigned j) const { return data[i+j*Dim1]; }

const InPlaceMatrix& operator*=(const T &b)

{

for(unsigned i = 0;i data *= b;

return *this;

}

const InPlaceMatrix& operator/=(const T &b)

{

for(unsigned i = 0;i data /= b;

return *this;

}

template

const InPlaceMatrix& operator+=(const MatrixExpr& a)

{

const A& ao ( a );

for(unsigned j = 0;j for(unsigned i = 0;i operator()(i,j) += ao(i,j);

return *this;

}

template

const InPlaceMatrix& operator-=(const MatrixExpr& a)

{

const A& ao ( a );

for(unsigned j = 0;j for(unsigned i = 0;i operator()(i,j) -= ao(i,j);

return *this;

}

template

const InPlaceMatrix& operator*=(const MatrixExpr& a)

{

const A& ao ( a );

*this = eval(*this * a);

return *this;

}

template

InPlaceMatrix& operator=(const MatrixExpr& a)

{

const A& ao ( a );

for(unsigned j = 0;j for(unsigned i = 0;i operator()(i,j) = ao(i,j);

return *this;

}

private:

T *const data;

};

template

InPlaceMatrix MakeMatrix(T *const p)

{

return InPlaceMatrix(p);

}

template

inline Matrix eval(const MatrixExpr& a)

2