Solve Matrix

Back to Home

Introduction:

solve is a modified version of matrix2 program.

It can efficiently solve matrix systems.

About:

solve program by Hamid Soltani. (gmail: hsoltanim)

Code:

Download solve.cpp

/*
solve is a modified version of matrix2 program.
It can efficiently solve matrix systems.
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 > 
#include < time.h >

using namespace std;

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

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

inline void getMatrixRC(Index i, Dimension& r, Dimension& c)
{
	union Access m;
	m.i = i;
	r = m.at.r;
	c = m.at.c;
}

static double lastDet; // Determinant computed by Inv function

					   // error codes
#define MER_OUT_OF_RANGE 1
#define MER_INVALID_DIMS 2
#define MER_ZERO_DET 3
#define MER_NOT_SQUARE 4

// a simple exception class
class Exception
{
public:
	const int code;
	Exception(const int arg) : code(arg) {	}
};

struct MatrixElem
{
public:
	map< Index, double >::iterator it;
	Dimension c;
	Dimension r;
	double v;
	bool good;
};

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

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

	// constructor
	Matrix(const Dimension row_count, const Dimension 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)
			throw Exception(MER_OUT_OF_RANGE);
		auto it = mp.find(getMatrixIndex(r, c));
		return ((it != mp.end()) ? it->second : 0.0);
	}

	// set matrix element
	void set(const Dimension r, const Dimension c, const double v)
	{
		if (r <= 0 || r > rows || c <= 0 || c > cols)
			throw Exception(MER_OUT_OF_RANGE);
		if (v == 0.0)
			mp.erase(getMatrixIndex(r, c));
		else
			mp[getMatrixIndex(r, c)] = v;
	}

	// begin iteration, next element after [r, c]
	void setIter(MatrixElem& me, const Dimension r, const Dimension c)
	{
		me.it = mp.upper_bound(getMatrixIndex(r, c));
		if (me.good = (me.it != mp.end()))
		{
			getMatrixRC(me.it->first, me.r, me.c);
			me.v = me.it->second;
		}
	}

	// next iteration
	void incIter(MatrixElem& me)
	{
		if (me.good = (++me.it != mp.end()))
		{
			getMatrixRC(me.it->first, me.r, me.c);
			me.v = me.it->second;
		}
	}

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

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

	// returns the number of columns
	inline 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
	inline 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 diagonal matrix with size n x n with ones at the diagonal
Matrix Diag(const Dimension n, const double v = 1.00)
{
	Matrix res = Matrix(n, n);
	for (Dimension i = 1; i <= n; i++)
		res.set(i, i, v);
	return res;
}

// returns a diagonal matrix
Matrix Diag(Matrix& v)
{
	Matrix res;
	if (v.GetCols() != 1 && v.GetRows() != 1)
		throw Exception(MER_INVALID_DIMS);
	res = (v.GetCols() == 1) ? Matrix(v.GetRows(), v.GetRows()) : Matrix(v.GetCols(), v.GetCols());
	MatrixElem me;
	for (v.setIter(me, 0, 0);me.good;v.incIter(me))
		res.set(me.r + me.c - 1, me.r + me.c - 1, me.v);
	return res;
}

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

	if (rows != cols)
	{
		throw Exception(MER_NOT_SQUARE);
		return res;
	}
	// calculate inverse using gauss-jordan elimination
	res = Diag(rows);   // a diagonal matrix with ones at the diagonal
	Matrix ai = a;    // make a copy of Matrix a
	lastDet = 1.0;
	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)
		{
			lastDet = 0.0;
			throw Exception(MER_ZERO_DET);
			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);
			}

		// 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 = c; s <= cols; s++)
						ai.set(r, s, ai(r, s) + f *ai(c, s));
					for (Dimension s = 1; s <= cols; s++)
						res.set(r, 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);
				lastDet *= f;
				for (Dimension s = c; s <= cols; s++)
					ai.set(r, s, ai(r, s) / f);
				for (Dimension s = 1; s <= cols; s++)
					res.set(r, s, res(r, s) / f);
			}
		}
	}
	return res;
}

// Solve equation a*x=v, a is n*n matrix and x,v are n*eqn matrices, eqn: number of equation sets
Matrix Solve(const Matrix& a, const Matrix& v)
{
	Matrix vi;
	Dimension n = a.GetRows();

	if (a.GetCols() != n || v.GetRows() != n)
	{
		throw Exception(MER_INVALID_DIMS);
		return vi;
	}

	Matrix ai = a;
	vi = v;
	Dimension eqn = v.GetCols();

	for (Dimension c = 1; c <= n; c++)
	{
		Dimension r;
		for (r = c; r <= n && ai(r, c) == 0.0; r++) {}
		if (r > n)
		{
			throw Exception(MER_ZERO_DET);
			return vi;
		}
		if (r != c)
		{
			for (Dimension s = 1; s <= n; s++)
			{
				double temp = ai(c, s);
				ai.set(c, s, ai(r, s));
				ai.set(r, s, temp);
			}
			for (Dimension eq = 1; eq <= eqn; eq++)
			{
				double temp = vi(c, eq);
				vi.set(c, eq, vi(r, eq));
				vi.set(r, eq, temp);
			}
		}

		double tc = ai(c, c);
		for (Dimension r = c + 1; r <= n; r++)
		{
			double t = ai(r, c);
			if (t != 0.0)
			{
				double f = -t / tc;
				ai.set(r, c, 0.0);
				MatrixElem me;
				for (ai.setIter(me, c, c);me.good && me.r == c;ai.incIter(me))
					ai.set(r, me.c, ai(r, me.c) + f *me.v);
				for (vi.setIter(me, c, 0);me.good && me.r == c;vi.incIter(me))
					vi.set(r, me.c, vi(r, me.c) + f *me.v);
			}
		}
	}
	for (Dimension r = n;r >= 1;r--)
		for (Dimension eq = 1; eq <= eqn; eq++)
		{
			double temp = 0.0;
			MatrixElem me;
			for (ai.setIter(me, r, r);me.good && me.r == r;ai.incIter(me))
				temp += me.v*vi(me.c, eq);
			vi.set(r, eq, (vi(r, eq) - temp) / ai(r, r));
		}
	return vi;
}

// Addition of Matrix with Matrix
Matrix Add(Matrix& a, Matrix& b)
{
	if (a.GetRows() != b.GetRows() || a.GetCols() != b.GetCols())
	{
		throw Exception(MER_INVALID_DIMS);
		return Matrix();
	}
	Matrix res(a.GetRows(), a.GetCols());
	MatrixElem me1, me2;
	a.setIter(me1, 0, 0);
	b.setIter(me2, 0, 0);
	while (me1.good || me2.good)
	{
		if (me1.good && me2.good && (me1.it)->first == (me2.it)->first)
		{
			res.set(me1.r, me1.c, me1.v + me2.v);
			a.incIter(me1);
			b.incIter(me2);
		}
		if (!me2.good || (me1.it)->first < (me2.it)->first)
		{
			res.set(me1.r, me1.c, me1.v);
			a.incIter(me1);
		}
		if (!me1.good || (me1.it)->first > (me2.it)->first)
		{
			res.set(me2.r, me2.c, me2.v);
			b.incIter(me2);
		}
	}
	return res;
}

// Subtraction of Matrix with Matrix
Matrix Sub(Matrix& a, Matrix& b)
{
	if (a.GetRows() != b.GetRows() || a.GetCols() != b.GetCols())
	{
		throw Exception(MER_INVALID_DIMS);
		return Matrix();
	}
	Matrix res(a.GetRows(), a.GetCols());
	MatrixElem me1, me2;
	a.setIter(me1, 0, 0);
	b.setIter(me2, 0, 0);
	while (me1.good || me2.good)
	{
		if (me1.good && me2.good && (me1.it)->first == (me2.it)->first)
		{
			res.set(me1.r, me1.c, me1.v - me2.v);
			a.incIter(me1);
			b.incIter(me2);
		}
		if (!me2.good || (me1.it)->first < (me2.it)->first)
		{
			res.set(me1.r, me1.c, me1.v);
			a.incIter(me1);
		}
		if (!me1.good || (me1.it)->first >(me2.it)->first)
		{
			res.set(me2.r, me2.c, -me2.v);
			b.incIter(me2);
		}
	}
	return res;
}

// Multiplication of Matrix with Matrix
Matrix Mul(Matrix& a, Matrix& b)
{
	Dimension ar = a.GetRows();
	Dimension bc = b.GetCols();

	if (a.GetCols() != b.GetRows())
	{
		throw Exception(MER_INVALID_DIMS);
		return Matrix();
	}

	Matrix res(ar, bc);
	for (Dimension r = 1; r <= ar; r++)
		for (Dimension m = 1;m <= bc;m++)
		{
			MatrixElem me;
			double temp = 0.0;
			for (a.setIter(me, r, 0);me.good && me.r == r;a.incIter(me))
				temp += me.v*b(me.c, m);
			res.set(r, m, temp);
		}
	return res;
}

// Transpose of Matrix
Matrix Trans(Matrix& a)
{
	Matrix res(a.GetCols(), a.GetRows());
	MatrixElem me;
	for (a.setIter(me, 0, 0);me.good;a.incIter(me))
		res.set(me.c, me.r, me.v);
	return res;
}

// Multiplication of Matrix with Transpose of a Matrix
Matrix MulTrans(Matrix& a, Matrix& b)
{
	Dimension ar = a.GetRows();
	Dimension br = b.GetRows();

	if (a.GetCols() != b.GetCols())
	{
		throw Exception(MER_INVALID_DIMS);
		return Matrix();
	}

	Matrix res(ar, br);
	for (Dimension r = 1; r <= ar; r++)
	{
		for (Dimension m = 1;m <= br;m++)
		{
			double temp = 0.0;
			MatrixElem me1, me2;
			a.setIter(me1, r, 0);
			b.setIter(me2, m, 0);
			while (me1.good && me2.good && (me1.r == r) && (me2.r == m))
				if (me1.c == me2.c)
				{
					temp += me1.v*me2.v;
					a.incIter(me1);
					b.incIter(me2);
				}
				else if(me1.c < me2.c)
					a.incIter(me1);
				else
					b.incIter(me2);
				res.set(r, m, temp);
		}
	}
	return res;
}

int main(int argc, char *argv[])
{
	// below some demonstration of the usage of the Matrix class
	try
	{
		// examples part 1
		Dimension cols = 3;
		Dimension rows = 3;
		Matrix A = Matrix(cols, rows);
		int count = 0;
		for (Dimension r = 1; r <= rows; r++)
			for (Dimension c = 1; c <= cols; c++)
				A.set(r, c, ++count);
		A.set(2, 1, 2);
		cout << "A= \n" << A << "\n";
		Matrix V = Matrix(rows, 1);
		V.set(1, 1, 10);
		V.set(2, 1, 22);
		V.set(3, 1, 46);
		cout << "V= \n" << V << "\n";
		cout << "Inv(A)*V= \n" << Mul(Inv(A), V) << "\n";
		cout << "Solve(A, V)= \n" << Solve(A, V) << "\n";
		cout << "\n\n\n";

		// examples part 2
		Dimension nadd = 5;
		Matrix A1 = Matrix(nadd, nadd);
		Matrix A2 = Matrix(nadd, nadd);
		count = 0;
		for (Dimension r = 1; r <= nadd; r++)
		{
			A1.set(r, 1, ++count);
			A2.set(r, r, ++count);
		}
		cout << "A1= \n" << A1 << "\n";
		cout << "A2= \n" << A2 << "\n";
		Matrix A3 = Sub(A1, A2);
		cout << "A1-A2= \n" << A3 << "\n";
		cout << "\n\n\n";

		// examples part 3
		Dimension test = 50;
		Dimension band = 5;
		cout << "Creating a " << test << "*" << test
			<< " Matrix with " << 2 * band + 1
			<< " non-zero middle elements at each row"
			<< "\nComputing Inverse and Solve, please wait...";
		Matrix M = Matrix(test, test);
		Matrix N = Matrix(test, 1);
		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);
			N.set(i, 1, 1.0 + rand() % 10);
		}
		clock_t t1 = clock();
		Matrix X1 = Mul(Inv(M), N);
		clock_t t2 = clock();
		Matrix X2 = Solve(M, N);
		clock_t t3 = clock();
		cout << "\nInv(M)*N, Solve(M, N)= \n";
		for (Dimension i = 1;i <= test;i++)
			cout << "(" << X1(i, 1) << ", " << X2(i, 1) << ") " << (!(i % 5) ? "\n" : "");
		cout << "\n\nInv(M)*N computation time: " << (double)(t2 - t1) / CLOCKS_PER_SEC;
		cout << "\nSolve(M, N) computation time: " << (double)(t3 - t2) / CLOCKS_PER_SEC;

		// examples part 4
		cout << "\n\n\ncomputing M*M and M*Trans(Trans(M)), please wait...";
		t1 = clock();
		X1 = Mul(M, M);
		t2 = clock();
		X2 = MulTrans(M, Trans(M));
		t3 = clock();
		cout << "\n\nM*M computation time: " << (double)(t2 - t1) / CLOCKS_PER_SEC;
		cout << "\nM*Trans(Trans(M)) computation time: " << (double)(t3 - t2) / CLOCKS_PER_SEC;
		cout << "\n\n";

		// examples part 5
		cout << "\n\n\ncomputing M*Inv(M) using Solve func, please wait...";
		t1 = clock();
		Matrix MMI = Mul(M, Solve(M, Diag(test)));
		t2 = clock();
		cout << "\n\nM*Inv M computation time: " << (double)(t2 - t1) / CLOCKS_PER_SEC;
		double e1 = 1e300;
		double d1 = e1;
		double e2 = -e1;
		double d2 = e2;
		for (int i = 1; i <= test;i++)
			for (int j = 1; j <= test;j++)
				if (i == j)
				{
					if (d1 > MMI(i, j)) d1 = MMI(i, j);
					if (d2 < MMI(i, j)) d2 = MMI(i, j);
				}
				else {
					if (e1 > MMI(i, j)) e1 = MMI(i, j);
					if (e2 < MMI(i, j)) e2 = MMI(i, j);
				}
		typedef numeric_limits< double > dbl;
		cout << setprecision(dbl::max_digits10)
			<< "\n\nDiagonal elements ranging from " << d1 << " to " << d2
			<< "\nNon-diagonal elements ranging from " << e1 << " to " << e2;
		cout << "\n\n";
	}
	catch (Exception err)
	{
		cout << "Error code " << err.code << endl;
	}
	catch (...)
	{
		cout << "An error occured...\n";
	}
	cout << "Press Enter to exit...\n";
	getchar();
	return EXIT_SUCCESS;
}