Matrix 2 in C++

Back to Home

Introduction:

matrix2 is a modified version of matrix program.

It uses std::map to store matrix elements and only stores non-zero elements.

It ignores unimportant bits of double numbers which results an absolute zero values for elements of M*Inv(M) not located on diagonal.

About:

matrix 2 program by Hamid Soltani. (gmail: hsoltanim)

Code:

Download matrix2.cpp

/*
matrix2 is a modified version of matrix program.
It uses std::map to store matrix elements and only stores non-zero elements.
It ignores unimportant bits of double numbers which results an absolute zero 
values for elements of M*Inv(M) not located on diagonal.
Original matrix program by Jos de Jong, Nov 2007. Updated March 2010
Modified by by Hamid Soltani. (gmail: hsoltanim)
https://csvparser.github.io/
Last modified: Sep. 2016.
*/

#include "stdafx.h"

#include < cstdlib >
#include < cstdio >
#include < math.h >
#include < map >

#include < iostream >
#include < iomanip > 

using namespace std;

#define unImpBits 24

union bits64 {
	double x;
	unsigned long long i;
};

bool isequal(double a, double b)
{
	bits64 mx;
	bits64 mn;
	mx.x = a;
	mn.x = b;
	if (mx.i < mn.i)
	{
		mx.x = b;
		mn.x = a;
	}
	if ((mx.i << 1) == 0) mx.i = 0;
	if ((mn.i << 1) == 0) mn.i = 0;
	return (mx.i - mn.i <= (1 << unImpBits));
}

double add(double x, double y)
{
	if (isequal(x,-y))
		return 0.0;
	else
		return x + y;
}

double sub(double x, double y)
{
	if (isequal(x, y))
		return 0.0;
	else
		return x - y;
}

// Declarations
class Matrix;
static double lastDet; // Determinant computed by Inv function
Matrix Diag(const int n);
Matrix Diag(const Matrix& v);
Matrix Inv(const Matrix& a);
Matrix Ones(const int rows, const int cols);
Matrix Zeros(const int rows, const int cols);

using Dimension = unsigned long int;
using Index = unsigned long long int;
union Access {
	Index i;
	struct {
		Dimension c;
		Dimension r;
	} at;
};

Index getMatrixIndex(Dimension r, Dimension c)
{
	union Access m;
	m.at.r = r;
	m.at.c = c;
	return m.i;
}

/*
* a simple exception class
* you can create an exeption by entering: throw Exception("...Error description...");
* and get the error message from the data msg for displaying: err.msg
*/
class Exception
{
public:
	const char* msg;
	Exception(const char* arg) : msg(arg) {	}
};


class Matrix
{
private:
	Dimension rows;
	Dimension cols;
	map< Index, double > mp;

public:
	// constructor
	Matrix()
	{
		rows = 0;
		cols = 0;
	}

	// constructor
	Matrix(const int row_count, const int column_count)
	{
		// create a Matrix object with given number of rows and columns
		rows = row_count;
		cols = column_count;
	}

	// assignment operator
	Matrix(const Matrix& a)
	{
		rows = a.rows;
		cols = a.cols;
		mp = a.mp;
	}

	// index operator. You can use this class like myMatrix(col, row)
	// the indexes are one-based, not zero based.
	const double operator()(const Dimension r, const Dimension c) const
	{
		if (r > 0 && r <= rows && c > 0 && c <= cols)
		{
			auto it = mp.find(getMatrixIndex(r, c));
			return ((it != mp.end()) ? it->second : 0.0);
		} else
			throw Exception("Subscript out of range");
	}

	// index operator. You can use this class like myMatrix(col, row)
	// the indexes are one-based, not zero based.
	// use this function get if you want to read from a const Matrix
	double get(const Dimension r, const Dimension c) const
	{
		if (r > 0 && r <= rows && c > 0 && c <= cols)
		{
			auto it = mp.find(getMatrixIndex(r, c));
			if (it != mp.end()) return it->second; else return 0.0;
		}
		else
			throw Exception("Subscript out of range");
	}

	void set(const Dimension r, const Dimension c, const double v)
	{
		if (r > 0 && r <= rows && c > 0 && c <= cols)
			if (v == 0.0) mp.erase(getMatrixIndex(r, c)); else mp[getMatrixIndex(r, c)] = v;
		else
			throw Exception("Subscript out of range");
	}

	// assignment operator
	Matrix& operator= (const Matrix& a)
	{
		rows = a.rows;
		cols = a.cols;
		mp = a.mp;
		return *this;
	}

	// add a double value (elements wise)
	Matrix& Add(const double v)
	{
		for (Dimension r = 1; r <= rows; r++)
			for (Dimension c = 1; c <= cols; c++)
				set(r, c, add(get(r, c), v));
		return *this;
	}

	// subtract a double value (elements wise)
	Matrix& Subtract(const double v)
	{
		return Add(-v);
	}

	// multiply a double value (elements wise)
	Matrix& Multiply(const double v)
	{
		for (Dimension r = 1; r <= rows; r++)
			for (Dimension c = 1; c <= cols; c++)
				set(r, c, get(r, c) * v);
		return *this;
	}

	// divide a double value (elements wise)
	Matrix& Divide(const double v)
	{
		return Multiply(1 / v);
	}

	// addition of Matrix with Matrix
	friend Matrix operator+(const Matrix& a, const Matrix& b)
	{
		// check if the dimensions match
		if (a.rows == b.rows && a.cols == b.cols)
		{
			Matrix res(a.rows, a.cols);
			for (Dimension r = 1; r <= a.rows; r++)
				for (Dimension c = 1; c <= a.cols; c++)
					res.set(r, c, add(a(r, c), b(r, c)));
			return res;
		}
		else
			throw Exception("Dimensions does not match");
		// return an empty matrix (this never happens but just for safety)
		return Matrix();
	}

	// addition of Matrix with double
	friend Matrix operator+ (const Matrix& a, const double b)
	{
		Matrix res = a;
		res.Add(b);
		return res;
	}
	// addition of double with Matrix
	friend Matrix operator+ (const double b, const Matrix& a)
	{
		Matrix res = a;
		res.Add(b);
		return res;
	}

	// subtraction of Matrix with Matrix
	friend Matrix operator- (const Matrix& a, const Matrix& b)
	{
		// check if the dimensions match
		if (a.rows == b.rows && a.cols == b.cols)
		{
			Matrix res(a.rows, a.cols);
			for (Dimension r = 1; r <= a.rows; r++)
				for (Dimension c = 1; c <= a.cols; c++)
					res.set(r, c, sub(a(r, c), b(r, c)));
			return res;
		}
		else
			throw Exception("Dimensions does not match");
		// return an empty matrix (this never happens but just for safety)
		return Matrix();
	}

	// subtraction of Matrix with double
	friend Matrix operator- (const Matrix& a, const double b)
	{
		Matrix res = a;
		res.Subtract(b);
		return res;
	}
	// subtraction of double with Matrix
	friend Matrix operator- (const double b, const Matrix& a)
	{
		Matrix res = -a;
		res.Add(b);
		return res;
	}

	// operator unary minus
	friend Matrix operator- (const Matrix& a)
	{
		Matrix res(a.rows, a.cols);
		for (Dimension r = 1; r <= a.rows; r++)
			for (Dimension c = 1; c <= a.cols; c++)
				res.set(r, c, -a(r, c));
		return res;
	}

	// operator multiplication
	friend Matrix operator* (const Matrix& a, const Matrix& b)
	{
		// check if the dimensions match
		if (a.cols == b.rows)
		{
			Matrix res(a.rows, b.cols);
			for (Dimension r = 1; r <= a.rows; r++)
				for (Dimension m = 1; m <= b.cols; m++)
				{
					double temp = 0.0;
					for (Dimension c = 1; c <= b.rows; c++)
						temp = add(temp, a(r, c) * b(c, m));
					res.set(r, m, temp);
				}
			return res;
		}
		else
			throw Exception("Dimensions does not match");
		// return an empty matrix (this never happens but just for safety)
		return Matrix();
	}

	// multiplication of Matrix with double
	friend Matrix operator* (const Matrix& a, const double b)
	{
		Matrix res = a;
		res.Multiply(b);
		return res;
	}
	// multiplication of double with Matrix
	friend Matrix operator* (const double b, const Matrix& a)
	{
		Matrix res = a;
		res.Multiply(b);
		return res;
	}

	// division of Matrix with Matrix
	friend Matrix operator/ (const Matrix& a, const Matrix& b)
	{
		// check if the dimensions match: must be square and equal sizes
		if (a.rows == a.cols && a.rows == a.cols && b.rows == b.cols)
		{
			Matrix res(a.rows, a.cols);
			res = a * Inv(b);
			return res;
		}
		else
			throw Exception("Dimensions does not match");
		// return an empty matrix (this never happens but just for safety)
		return Matrix();
	}

	// division of Matrix with double
	friend Matrix operator/ (const Matrix& a, const double b)
	{
		Matrix res = a;
		res.Divide(b);
		return res;
	}

	// division of double with Matrix
	friend Matrix operator/ (const double b, const Matrix& a)
	{
		Matrix b_matrix(1, 1);
		b_matrix.set(1, 1, b);
		Matrix res = b_matrix / a;
		return res;
	}

	// returns the number of rows
	Dimension GetRows() const
	{
		return rows;
	}

	// returns the number of columns
	Dimension GetCols() const
	{
		return cols;
	}

	// output operator
	friend ostream& operator<<(ostream& os, const Matrix& M)
	{
		for (Dimension r = 1; r <= M.rows; r++)
			for (Dimension c = 1; c <= M.cols; c++)
				os << ((c == 1) ? ((r == 1) ? "[" : " ") : "")
					<< setw(8) << setprecision(4) << M(r, c)
					<< ((c == M.cols) ? ((r == M.rows) ? "]" : ";\n") : ",");
		return os;
	}
	// Number of non-zero elements
	Index Size()
	{
		return mp.size();
	}
	// destructor
	~Matrix()
	{
		mp.clear();
	}

};

/**
* returns a matrix with size cols x rows with ones as values
*/
Matrix Ones(const Dimension rows, const Dimension cols)
{
	Matrix res = Matrix(rows, cols);
	for (Dimension r = 1; r <= rows; r++)
		for (Dimension c = 1; c <= cols; c++)
			res.set(r, c, 1.00);
	return res;
}

/**
* returns a matrix with size cols x rows with zeros as values
*/
Matrix Zeros(const Dimension rows, const Dimension cols)
{
	return Matrix(rows, cols);
}


/**
* returns a diagonal matrix with size n x n with ones at the diagonal
* @param  v a vector
* @return a diagonal matrix with ones on the diagonal
*/
Matrix Diag(const Dimension n)
{
	Matrix res = Matrix(n, n);
	for (Dimension i = 1; i <= n; i++)
		res.set(i, i, 1.00);
	return res;
}

/**
* returns a diagonal matrix
* @param  v a vector
* @return a diagonal matrix with the given vector v on the diagonal
*/
Matrix Diag(const Matrix& v)
{
	Matrix res;
	if (v.GetCols() == 1)
	{
		// the given matrix is a vector n x 1
		Dimension rows = v.GetRows();
		res = Matrix(rows, rows);
		// copy the values of the vector to the matrix
		for (Dimension r = 1; r <= rows; r++)
			res.set(r, r, v(r, 1));
	}
	else if (v.GetRows() == 1)
	{
		// the given matrix is a vector 1 x n
		Dimension cols = v.GetCols();
		res = Matrix(cols, cols);
		// copy the values of the vector to the matrix
		for (Dimension c = 1; c <= cols; c++)
			res.set(c, c, v(1, c));
	}
	else
		throw Exception("Parameter for diag must be a vector");
	return res;
}

/*
* returns the inverse of Matrix a, stores determinent in DT
*/
Matrix Inv(const Matrix& a, double& DT)
{
	Matrix res;
	Dimension rows = a.GetRows();
	Dimension cols = a.GetCols();

	if (rows != cols)
	{
		throw Exception("Matrix must be square");
		return res;
	}
	// this is a matrix of 3 x 3 or larger
	// calculate inverse using gauss-jordan elimination
	//   http://mathworld.wolfram.com/MatrixInverse.html
	//   http://math.uww.edu/~mcfarlat/inverse.htm
	res = Diag(rows);   // a diagonal matrix with ones at the diagonal
	Matrix ai = a;    // make a copy of Matrix a
	DT = 1;
	for (Dimension c = 1; c <= cols; c++)
	{
		// element (c, c) should be non zero. if not, swap content
		// of lower rows
		Dimension r;
		for (r = c; r <= rows && ai(r, c) == 0.0; r++) {}
		if (r >rows)
		{
			DT = 0;
			throw Exception("Determinant of matrix is zero");
			return res;
		}
		if (r != c)
			// swap rows
			for (Dimension s = 1; s <= cols; s++)
			{
				double temp = ai(c, s);
				ai.set(c, s, ai(r, s));
				ai.set(r, s, temp);
				temp = res(c, s);
				res.set(c, s, res(r, s));
				res.set(r, s, temp);
				//Swap(ai(c, s), ai(r, s));
				//Swap(res(c, s), res(r, s));
			}

		// eliminate non-zero values on the other rows at column c
		for (Dimension r = 1; r <= rows; r++)
		{
			if (r != c)
			{
				// eleminate value at column c and row r
				if (ai(r, c) != 0.0)
				{
					double f = -ai(r, c) / ai(c, c);

					// add (f * row c) to row r to eleminate the value
					// at column c
					for (Dimension s = 1; s <= cols; s++)
					{
						ai.set(r, s, add(ai(r, s), f *ai(c, s)));
						res.set(r, s, add(res(r, s), f *res(c, s)));
						//ai(r, s) += f * ai(c, s);
						//res(r, s) += f * res(c, s);
					}
				}
			}
			else
			{
				// make value at (c, c) one,
				// divide each value on row r with the value at ai(c,c)
				double f = ai(c, c);
				DT *= f;
				for (Dimension s = 1; s <= cols; s++)
				{
					ai.set(r, s, ai(r, s) / f);
					res.set(r, s, res(r, s) / f);
					//ai(r, s) /= f;
					//res(r, s) /= f;
				}
			}
		}
	}
	return res;
}
/*
* returns the inverse of Matrix a, stores determinent in lastDet
*/
Matrix Inv(const Matrix& a)
{
	return Inv(a, lastDet);
}

int main(int argc, char *argv[])
{
	// below some demonstration of the usage of the Matrix class
	try
	{
		// create an empty matrix of 3x3 (will initially contain zeros)
		Dimension cols = 3;
		Dimension rows = 3;
		Matrix A = Matrix(cols, rows);

		// fill in some values in matrix a
		int count = 0;
		for (Dimension r = 1; r <= rows; r++)
			for (Dimension c = 1; c <= cols; c++)
				A.set(r, c, ++count);

		// adjust a value in the matrix (indexes are one-based)
		A.set(2, 1, 1.23);

		// print the whole matrix
		cout << "A= \n" << A << "\n";
		cout << A(2, 1) << "\n";

		Matrix B = Ones(rows, cols)+Diag(rows);
		cout << "B= \n" << B << "\n";

		Matrix B2 = Matrix(rows, 1);    // a vector
		count = 1;
		for (Dimension r = 1; r <= rows; r++)
			B2.set(r, 1, ++count * 2);
		cout << "B2= \n" << B2 << "\n";

		cout << "A + B= \n" << A + B << "\n";
		cout << "A - B= \n" << A - B << "\n";
		cout << "A * B2= \n" << A * B2 << "\n";

		cout << "Diag. B2= \n" << Diag(B2) << "\n";

		cout << "A= \n" << A << "\n";
		cout << "3 - A= \n" << 3 - A << "\n";
		Matrix A_inv = Inv(A);
		cout << "Inv(A)= \n" << A_inv << "\n";
		cout << "Det(A)=" << lastDet << "\n";
		cout << "A * Inv(A)= \n" << A * Inv(A) << "\n";

		cout << "B / A = \n" << B / A << "\n";
		cout << "A / 3 = \n" << A / 3 << "\n";

		rows = 2;
		cols = 5;
		Matrix H = Matrix(rows, cols);
		for (Dimension r = 1; r <= rows; r++)
			for (Dimension c = 1; c <= cols; c++)
				H.set(r, c, ++count);
		cout << "H= \n" << H << "\n";

		cout << "\n\n\n";

		Dimension test = 100;
		Dimension band = 5;
		cout << "Creating a " << test << "*" << test 
			<< " Matrix with " << 2 * band + 1
			<< " non-zero middle elements at each row, please wait...";

		Matrix M = Matrix(test, test);
		for (Dimension i = 1;i <= test;i++)
		{
			Dimension a = (i <= band) ? 1 : i - band;
			Dimension b = (i > test - band) ? test : i + band;
			for (Dimension j = a;j <= b;j++)
				M.set(i, j, 1.0 + rand() % 10);
		}
		cout << "\n\nNumber of non-zero elements of Matrix M: " << M.Size() << "\n\n";
		cout << "Computing Inv M, please wait...";
		Matrix MI = Inv(M);
		cout << "\nNumber of non-zero elements of Matrix Inv M: " << MI.Size() << "\n\n";
		cout << "Computing M*Inv M, please wait...";
		Matrix MMI = M*Inv(M);
		cout << "\nNumber of non-zero elements of Matrix M*Inv M: " << MMI.Size() << "\n\n";
	}
	catch (Exception err)
	{
		cout << "Error: \n" << err.msg << endl;
	}
	catch (...)
	{
		cout << "An error occured...\n";
	}
	cout << "Press Enter to exit...\n";
	getchar();
	return EXIT_SUCCESS;
}