#include #include #include #include "tools.hpp" template matrix::matrix( double constant ) { for ( unsigned int i{ 0 }; i < m; ++i ) { for ( unsigned int j{ 0 }; j < n; ++j ) { entries_[i][j] = constant; } } } template matrix::matrix( std::initializer_list> init ) { unsigned int i{ 0 }; for ( std::initializer_list>::iterator itr1{ init.begin() }; (itr1 != init.end()) && (i < m); ++itr1, ++i ) { unsigned int j{ 0 }; for ( std::initializer_list::iterator itr2{ itr1->begin() }; (itr2 != itr1->end()) && (j < n); ++itr2, ++j ) { entries_[i][j] = *itr2; } for ( ; j < n; ++j ) { entries_[i][j] = 0.0; } } for ( ; i < m; ++i ) { for ( unsigned int j{ 0 }; j < n; ++j ) { entries_[i][j] = 0.0; } } } template matrix::matrix( std::initializer_list> init ) { unsigned int j{ 0 }; for ( typename std::initializer_list>::iterator itr{ init.begin() }; (itr != init.end()) && (j < n); ++itr, ++j ) { for ( unsigned int i{ 0 }; i < m; ++i ) { entries_[i][j] = (*itr)( i ); } } for ( ; j < n; ++j ) { for ( unsigned int i{ 0 }; i < m; ++i ) { entries_[i][j] = 0.0; } } } // Construct a diagonal matrix with the entries of // the initializer list on the diagonal. template matrix::matrix( std::initializer_list init ) { unsigned int i{ 0 }; for ( std::initializer_list::iterator itr{ init.begin() }; (itr != init.end()) && (i < std::min( m, n )); ++itr, ++i ) { for ( unsigned int j{ 0 }; j < n; ++j ) { entries_[i][j] = 0.0; } entries_[i][i] = *itr; } for ( ; i < m; ++i ) { for ( unsigned int j{ 0 }; j < n; ++j ) { entries_[i][j] = 0.0; } } } template double &matrix::operator()( unsigned int i, unsigned int j ) { if ( (i >= m) || (j >= n) ) { throw_matrix_exception( i, j ); } return entries_[i][j]; } template double const &matrix::operator()( unsigned int i, unsigned int j ) const { if ( (i >= m) || (j >= n) ) { throw_matrix_exception( i, j ); } return entries_[i][j]; } template void matrix::throw_matrix_exception( unsigned int i, unsigned int j ) const { std::string article{ "a " }; unsigned int em{ m }; while ( em >= 1000 ) { em /= 1000; } if ( (em == 8) || (em == 18) || ((em >= 80) && (em <= 89)) || ((em >= 800) && (em <= 899)) ) { article = "an "; } throw std::out_of_range{ "The index (" + std::to_string( i ) + ", " + std::to_string( j ) + ") is beyond the dimensions of " + article + std::to_string( m ) + " x " + std::to_string( n ) + " matrix" }; } /*************************************************************** * Scalar multiplication ***************************************************************/ template matrix matrix::operator*( double s ) const { matrix product; for ( unsigned int i{0}; i < m; ++i ) { for ( unsigned int j{0}; i < n; ++j ) { product.entries_[i][j] = s*entries_[i][j]; } } return product; } template matrix &matrix::operator*=( double s ) { for ( unsigned int i{0}; i < m; ++i ) { for ( unsigned int j{0}; i < n; ++j ) { entries_[i][j] *= s; } } return *this; } template matrix matrix::operator/( double s ) const { matrix product; for ( unsigned int i{0}; i < m; ++i ) { for ( unsigned int j{0}; i < n; ++j ) { product.entries_[i][j] = entries_[i][j]/s; } } return product; } template matrix &matrix::operator/=( double s ) { for ( unsigned int i{0}; i < m; ++i ) { for ( unsigned int j{0}; i < n; ++j ) { entries_[i][j] /= s; } } return *this; } /*************************************************************** * Matrix addition ***************************************************************/ template matrix matrix::operator+( matrix const &A ) const { matrix sum; for ( unsigned int i{ 0 }; i < m; ++i ) { for ( unsigned int j{ 0 }; j < n; ++j ) { sum.entries_[i][j] = entries_[i][j] + A.entries_[i][j]; } } return sum; } template matrix matrix::operator-( matrix const &A ) const { matrix sum; for ( unsigned int i{ 0 }; i < m; ++i ) { for ( unsigned int j{ 0 }; j < n; ++j ) { sum.entries_[i][j] = entries_[i][j] - A.entries_[i][j]; } } return sum; } template matrix &matrix::operator+=( matrix const &A ) { for ( unsigned int i{ 0 }; i < m; ++i ) { for ( unsigned int j{ 0 }; j < n; ++j ) { entries_[i][j] += A.entries_[i][j]; } } return *this; } template matrix &matrix::operator-=( matrix const &A ) { for ( unsigned int i{ 0 }; i < m; ++i ) { for ( unsigned int j{ 0 }; j < n; ++j ) { entries_[i][j] -= A.entries_[i][j]; } } return *this; } /*************************************************************** * Scalar addition ***************************************************************/ template matrix matrix::operator+( double s ) const { matrix sum{ *this }; for ( int k{ 0 }; k < std::min( m, n ); ++k ) { sum.entries_[k][k] += s; } return *this; } template matrix &matrix::operator+=( double s ) { for ( int k{ 0 }; k < std::min( m, n ); ++k ) { entries_[k][k] += s; } return *this; } template matrix matrix::operator-( double s ) const { matrix sum{ *this }; for ( int k{ 0 }; k < std::min( m, n ); ++k ) { sum.entries_[k][k] -= s; } return *this; } template matrix &matrix::operator-=( double s ) { for ( int k{ 0 }; k < std::min( m, n ); ++k ) { entries_[k][k] -= s; } return *this; } /*************************************************************** * Unary operators ***************************************************************/ template matrix matrix::operator +() const { return *this; } template matrix matrix::operator -() const { return *this * (-1.0); } /************************* * Matrix-vector product * *************************/ template vec matrix::operator*( vec const &v ) const { vec product{}; for ( unsigned int i{ 0 }; i < m; ++i ) { for ( unsigned int j{ 0 }; j < n; ++j ) { product( i ) += entries_[i][j]*v( j ); } } return product; } template std::string matrix::to_string() const { if ( (m == 0) || (n == 0) ) { return std::string{ "[]" }; } else { std::string str{ "[" + std::to_string( entries_[0][0] ) }; for ( unsigned int j{ 1 }; j < n; ++j ) { str += " " + std::to_string( entries_[0][j] ); } for ( unsigned int i{ 1 }; i < m; ++i ) { str += "; " + std::to_string( entries_[i][0] ); for ( unsigned int j{ 1 }; j < n; ++j ) { str += " " + std::to_string( entries_[i][j] ); } } return str + "]"; } } template matrix matrix::operator*=( matrix const &A ) const { for ( unsigned int i{ 0 }; i < m; ++i ) { double result[n]; for ( unsigned int k{ 0 }; k < n; ++k ) { result[k] = 0.0; for ( unsigned int j{ 0 }; j < n; ++j ) { result[k] += entries_[i][j]*A.entries_[j][k]; } } for ( unsigned int k{ 0 }; k < n; ++k ) { entries_[i][k] = result[k]; } } return *this; } template matrix operator*( matrix const &B, matrix const &A ) { matrix result{ 0.0 }; for ( unsigned int i{ 0 }; i < ell; ++i ) { for ( unsigned int k{ 0 }; k < n; ++k ) { for ( unsigned int j{ 0 }; j < m; ++j ) { result.entries_[i][k] += B.entries_[i][j]*A.entries_[j][k]; } } } return result; } // * // The adjoint is usually written as A and as the transpose is // the manifestation of the adjoint for finite-dimensional real // matrices, it makes sense to override the *A operator to return // the transpose of A. template matrix matrix::operator*() const { matrix adjoint; for ( unsigned int i{ 0 }; i < m; ++i ) { for ( unsigned int j{ 0 }; j < n; ++j ) { adjoint(j, i) = (*this)(i, j); //adjoint.entries_[j][i] = entries_[i][j]; } } return adjoint; } // You can also call the transpose function. template matrix transpose( matrix const &A ) { return *A; } template std::ostream &operator<<( std::ostream &out, matrix const &rhs ) { return out << rhs.to_string(); } // If there are no solutions, then an empty std::vector is returned. // If there is exactly one solution, // then a std::vector with size '1' is returned, and // that one entry is the unique solution. // If there are infinitely many solutions, // and there are 'N' free variables (the dimension of the null // space is 'N'), then a std::vector with size 'N + 1' is // returned, entry '0' is a particular solution, and // entries '1' through 'N' are a basis for the null space; // thus, the first vector plus any linear combination of // the subsequent vectors are all solutions. template std::vector> matrix::solve( vec const &v, double tolerance ) const { // Create the augmented matrix double memory[m*(n + 1)]; double *Aaug[m]; for ( unsigned int i{ 0 }; i < m; ++i ) { Aaug[i] = memory + i*(n + 1); for ( unsigned int j{ 0 }; j < n; ++j ) { Aaug[i][j] = entries_[i][j]; } Aaug[i][n] = v( i ); } gaussian_elimination( Aaug, m, n + 1, tolerance ); bool no_solutions{ false }; bool free_list[n + 1]; unsigned int index_list[n + 1]; for ( unsigned int k{ 0 }; k < n; ++k ) { free_list[k] = false; index_list[k] = 0; } unsigned int rank{ analize_row_echelon_form( Aaug, free_list, index_list, m, n + 1, tolerance ) }; if ( !free_list[n] ) { // There are no solutions return std::vector>( 0 ); } else if ( rank == n ) { std::vector> u( 1 ); for ( unsigned int i{ m - 1 }; i < m; --i ) { u[0]( i ) = Aaug[i][n]; for ( unsigned int j{ i + 1 }; j < n; ++j ) { u[0]( i ) -= Aaug[i][j]*u[0]( j ); } u[0]( i ) /= Aaug[i][i]; } return u; } else { std::vector> u( n - rank + 1 ); for ( unsigned int j{ n - 1 }; j < n; --j ) { if ( free_list[j] ) { u[index_list[j] + 1]( j ) = 1.0; } else { u[0]( j ) = Aaug[index_list[j]][n]; for ( unsigned int k{ j + 1 }; k < n; ++k ) { if ( free_list[k] ) { u[index_list[k] + 1]( j ) -= Aaug[index_list[j]][k]/Aaug[index_list[j]][j]; } else { u[0]( j ) -= Aaug[index_list[j]][k]*u[0]( k ); for ( unsigned int ell{ k + 1 }; ell < n; ++ell ) { if ( free_list[ell] ) { u[index_list[ell] + 1]( j ) -= Aaug[index_list[j]][k]*u[index_list[ell] + 1]( k ) / Aaug[index_list[j]][j]; } } } } u[0]( j ) /= Aaug[index_list[j]][j]; } } return u; } } template unsigned int matrix::rank( double tolerance ) const { // Create a copy to modify double memory[m*n]; double *A[m]; for ( unsigned int i{ 0 }; i < m; ++i ) { A[i] = memory + i*n; for ( unsigned int j{ 0 }; j < n; ++j ) { A[i][j] = entries_[i][j]; } } return gaussian_elimination( A, n, n, tolerance ); } template double matrix::det( double tolerance ) const { if ( m != n ) { return 0.0; } // Create a copy to modify double memory[n*n]; double *A[n]; for ( unsigned int i{ 0 }; i < n; ++i ) { A[i] = memory + i*n; for ( unsigned int j{ 0 }; j < n; ++j ) { A[i][j] = entries_[i][j]; } } gaussian_elimination( A, n, n, tolerance ); double product{ 1.0 }; for ( unsigned int k{ 0 }; k < n; ++k ) { product *= A[k][k]; } return product; } template double matrix::tr() const { double sum{ 0.0 }; for ( unsigned int k{ 0 }; k < std::min( m, n ); ++k ) { sum += entries_[k][k]; } return sum; } template matrix matrix::inv( double tolerance ) const { if ( m != n ) { // Need to calculate the Moore-Penrose pseudo-inverse return matrix{}; } // Create the matrix ( A | I ) where we will perform // n // row operations to convert the matrix to the form // -1 // ( I | A ) and then extract the inverse. // n double memory[n*2*n]; double *A[n]; for ( unsigned int i{ 0 }; i < n; ++i ) { A[i] = memory + i*2*n; for ( unsigned int j{ 0 }; j < n; ++j ) { A[i][j] = entries_[i][j]; A[i][n + j] = 0.0; } A[i][n + i] = 1.0; } // First apply Gaussian elimination gaussian_elimination( A, n, 2*n, tolerance ); if ( A[n - 1][n - 1] == 0.0 ) { std::clog << "The matrix is not invertible..." << std::endl; } // The diagonal entries of the left-hand matrix are // still not 1, so divide Row i by entry (i,i) for ( unsigned int i{ 0 }; i < n; ++i ) { std::clog << "Multiplying Row " << i << " by " << 1.0/A[i][i] << std::endl; for ( unsigned int j{ i + 1 }; j < 2*n; ++j ) { A[i][j] /= A[i][i]; } A[i][i] = 1.0; // Technically not necessary } // The upper triangular component of the left-hand // matrix is still not all zeros, so perform row // operations to eliminate these. for ( unsigned int j{ n - 1 }; j > 0; --j ) { for ( unsigned int i{ 0 }; i < j; ++i ) { std::clog << "Adding " << (-A[i][j]/A[i][i]) << " times Row " << j << " onto Row " << i << std::endl; for ( unsigned int k{ j + 1 }; k < 2*n; ++k ) { A[i][k] -= A[i][j]/A[i][i] * A[j][k]; } A[i][j] = 0.0; } } // Create an n x n matrix that will store the entries // of the inverse of the matrix and copy them over matrix InvA{}; for ( unsigned int i{ 0 }; i < n; ++i ) { for ( unsigned int j{ 0 }; j < n; ++j ) { InvA.entries_[i][j] = A[i][n + j]; } } return InvA; } template unsigned int rank( matrix const &A ) { return A.rank(); } template double det( matrix const & A ) { return A.det(); } template double tr( matrix const & A ) { return A.tr(); } template matrix inv( matrix const & A ) { return A.inv(); }