Skip to content

Instantly share code, notes, and snippets.

@hucancode
Last active July 23, 2022 03:21
Show Gist options
  • Save hucancode/4102f15bdf9388ac0ccec7c0728557e4 to your computer and use it in GitHub Desktop.
Save hucancode/4102f15bdf9388ac0ccec7c0728557e4 to your computer and use it in GitHub Desktop.
portable matrix library, support addition, multiplication, transposition, inversion
#include <stdexcept>
using namespace std;
class Matrix {
public:
Matrix(int, int);
Matrix();
~Matrix();
Matrix(const Matrix&);
Matrix& operator=(const Matrix&);
inline double& operator()(int x, int y) { return p[x][y]; }
Matrix& operator+=(const Matrix&);
Matrix& operator-=(const Matrix&);
Matrix& operator*=(const Matrix&);
Matrix& operator*=(double);
Matrix& operator/=(double);
void swapRows(int, int);
Matrix transpose();
static Matrix createIdentity(int);
// functions on vectors
static double dotProduct(Matrix, Matrix);
// functions on augmented matrices
static Matrix augment(Matrix, Matrix);
Matrix gaussianEliminate();
Matrix rowReduceFromGaussian();
Matrix inverse();
private:
int rows_, cols_;
double **p;
void allocSpace();
};
Matrix operator+(const Matrix&, const Matrix&);
Matrix operator-(const Matrix&, const Matrix&);
Matrix operator*(const Matrix&, const Matrix&);
Matrix operator*(const Matrix&, double);
Matrix operator*(double, const Matrix&);
Matrix operator/(const Matrix&, double);
#define EPS 1e-10
using std::ostream; using std::istream; using std::endl;
using std::domain_error;
/* PUBLIC MEMBER FUNCTIONS
********************************/
Matrix::Matrix(int rows, int cols) : rows_(rows), cols_(cols)
{
allocSpace();
for (int i = 0; i < rows_; ++i) {
for (int j = 0; j < cols_; ++j) {
p[i][j] = 0;
}
}
}
Matrix::Matrix() : rows_(1), cols_(1)
{
allocSpace();
p[0][0] = 0;
}
Matrix::~Matrix()
{
for (int i = 0; i < rows_; ++i) {
delete[] p[i];
}
delete[] p;
}
Matrix::Matrix(const Matrix& m) : rows_(m.rows_), cols_(m.cols_)
{
allocSpace();
for (int i = 0; i < rows_; ++i) {
for (int j = 0; j < cols_; ++j) {
p[i][j] = m.p[i][j];
}
}
}
Matrix& Matrix::operator=(const Matrix& m)
{
if (this == &m) {
return *this;
}
if (rows_ != m.rows_ || cols_ != m.cols_) {
for (int i = 0; i < rows_; ++i) {
delete[] p[i];
}
delete[] p;
rows_ = m.rows_;
cols_ = m.cols_;
allocSpace();
}
for (int i = 0; i < rows_; ++i) {
for (int j = 0; j < cols_; ++j) {
p[i][j] = m.p[i][j];
}
}
return *this;
}
Matrix& Matrix::operator+=(const Matrix& m)
{
for (int i = 0; i < rows_; ++i) {
for (int j = 0; j < cols_; ++j) {
p[i][j] += m.p[i][j];
}
}
return *this;
}
Matrix& Matrix::operator-=(const Matrix& m)
{
for (int i = 0; i < rows_; ++i) {
for (int j = 0; j < cols_; ++j) {
p[i][j] -= m.p[i][j];
}
}
return *this;
}
Matrix& Matrix::operator*=(const Matrix& m)
{
Matrix temp(rows_, m.cols_);
for (int i = 0; i < temp.rows_; ++i) {
for (int j = 0; j < temp.cols_; ++j) {
for (int k = 0; k < cols_; ++k) {
temp.p[i][j] += (p[i][k] * m.p[k][j]);
}
}
}
return (*this = temp);
}
Matrix& Matrix::operator*=(double num)
{
for (int i = 0; i < rows_; ++i) {
for (int j = 0; j < cols_; ++j) {
p[i][j] *= num;
}
}
return *this;
}
Matrix& Matrix::operator/=(double num)
{
for (int i = 0; i < rows_; ++i) {
for (int j = 0; j < cols_; ++j) {
p[i][j] /= num;
}
}
return *this;
}
void Matrix::swapRows(int r1, int r2)
{
double *temp = p[r1];
p[r1] = p[r2];
p[r2] = temp;
}
Matrix Matrix::transpose()
{
Matrix ret(cols_, rows_);
for (int i = 0; i < rows_; ++i) {
for (int j = 0; j < cols_; ++j) {
ret.p[j][i] = p[i][j];
}
}
return ret;
}
/* STATIC CLASS FUNCTIONS
********************************/
Matrix Matrix::createIdentity(int size)
{
Matrix temp(size, size);
for (int i = 0; i < temp.rows_; ++i) {
for (int j = 0; j < temp.cols_; ++j) {
if (i == j) {
temp.p[i][j] = 1;
} else {
temp.p[i][j] = 0;
}
}
}
return temp;
}
// functions on VECTORS
double Matrix::dotProduct(Matrix a, Matrix b)
{
double sum = 0;
for (int i = 0; i < a.rows_; ++i) {
sum += (a(i, 0) * b(i, 0));
}
return sum;
}
// functions on AUGMENTED matrices
Matrix Matrix::augment(Matrix A, Matrix B)
{
Matrix AB(A.rows_, A.cols_ + B.cols_);
for (int i = 0; i < AB.rows_; ++i) {
for (int j = 0; j < AB.cols_; ++j) {
if (j < A.cols_)
AB(i, j) = A(i, j);
else
AB(i, j) = B(i, j - B.cols_);
}
}
return AB;
}
Matrix Matrix::gaussianEliminate()
{
Matrix Ab(*this);
int rows = Ab.rows_;
int cols = Ab.cols_;
int Acols = cols - 1;
int i = 0; // row tracker
int j = 0; // column tracker
// iterate through the rows
while (i < rows)
{
// find a pivot for the row
bool pivot_found = false;
while (j < Acols && !pivot_found)
{
if (Ab(i, j) != 0) { // pivot not equal to 0
pivot_found = true;
} else { // check for a possible swap
int max_row = i;
double max_val = 0;
for (int k = i + 1; k < rows; ++k)
{
double cur_abs = Ab(k, j) >= 0 ? Ab(k, j) : -1 * Ab(k, j);
if (cur_abs > max_val)
{
max_row = k;
max_val = cur_abs;
}
}
if (max_row != i) {
Ab.swapRows(max_row, i);
pivot_found = true;
} else {
j++;
}
}
}
// perform elimination as normal if pivot was found
if (pivot_found)
{
for (int t = i + 1; t < rows; ++t) {
for (int s = j + 1; s < cols; ++s) {
Ab(t, s) = Ab(t, s) - Ab(i, s) * (Ab(t, j) / Ab(i, j));
if (Ab(t, s) < EPS && Ab(t, s) > -1*EPS)
Ab(t, s) = 0;
}
Ab(t, j) = 0;
}
}
i++;
j++;
}
return Ab;
}
Matrix Matrix::rowReduceFromGaussian()
{
Matrix R(*this);
int rows = R.rows_;
int cols = R.cols_;
int i = rows - 1; // row tracker
int j = cols - 2; // column tracker
// iterate through every row
while (i >= 0)
{
// find the pivot column
int k = j - 1;
while (k >= 0) {
if (R(i, k) != 0)
j = k;
k--;
}
// zero out elements above pivots if pivot not 0
if (R(i, j) != 0) {
for (int t = i - 1; t >= 0; --t) {
for (int s = 0; s < cols; ++s) {
if (s != j) {
R(t, s) = R(t, s) - R(i, s) * (R(t, j) / R(i, j));
if (R(t, s) < EPS && R(t, s) > -1*EPS)
R(t, s) = 0;
}
}
R(t, j) = 0;
}
// divide row by pivot
for (int k = j + 1; k < cols; ++k) {
R(i, k) = R(i, k) / R(i, j);
if (R(i, k) < EPS && R(i, k) > -1*EPS)
R(i, k) = 0;
}
R(i, j) = 1;
}
i--;
j--;
}
return R;
}
Matrix Matrix::inverse()
{
Matrix I = Matrix::createIdentity(rows_);
Matrix AI = Matrix::augment(*this, I);
Matrix U = AI.gaussianEliminate();
Matrix IAInverse = U.rowReduceFromGaussian();
Matrix AInverse(rows_, cols_);
for (int i = 0; i < AInverse.rows_; ++i) {
for (int j = 0; j < AInverse.cols_; ++j) {
AInverse(i, j) = IAInverse(i, j + cols_);
}
}
return AInverse;
}
/* PRIVATE HELPER FUNCTIONS
********************************/
void Matrix::allocSpace()
{
p = new double*[rows_];
for (int i = 0; i < rows_; ++i) {
p[i] = new double[cols_];
}
}
/* NON-MEMBER FUNCTIONS
********************************/
Matrix operator+(const Matrix& m1, const Matrix& m2)
{
Matrix temp(m1);
return (temp += m2);
}
Matrix operator-(const Matrix& m1, const Matrix& m2)
{
Matrix temp(m1);
return (temp -= m2);
}
Matrix operator*(const Matrix& m1, const Matrix& m2)
{
Matrix temp(m1);
return (temp *= m2);
}
Matrix operator*(const Matrix& m, double num)
{
Matrix temp(m);
return (temp *= num);
}
Matrix operator*(double num, const Matrix& m)
{
return (m * num);
}
Matrix operator/(const Matrix& m, double num)
{
Matrix temp(m);
return (temp /= num);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment