/* * * Template Numerical Toolkit (TNT) * * Mathematical and Computational Sciences Division * National Institute of Technology, * Gaithersburg, MD USA * * * This software was developed at the National Institute of Standards and * Technology (NIST) by employees of the Federal Government in the course * of their official duties. Pursuant to title 17 Section 105 of the * United States Code, this software is not subject to copyright protection * and is in the public domain. NIST assumes no responsibility whatsoever for * its use by other parties, and makes no guarantees, expressed or implied, * about its quality, reliability, or any other characteristic. * */ // C compatible matrix: row-oriented, 0-based [i][j] and 1-based (i,j) indexing // #ifndef TNT_MATRIX_H #define TNT_MATRIX_H #include "tnt_subscript.h" #include "tnt_vector.h" #include #include #include #include #include namespace TNT { //namespace Linear_Algebra { /** Dense matrix class for basic linear algebra operations. Ordering: row major.

Elements begin at (1,1) or [0][0].

Can be interfaced with C multidimentionsal arrays (e.g. double **)

copy-by-value semantics.

Optional range checking at compile time via TNT_BOUNDS_CHECK macro. */ template class Matrix { private: Subscript m_; Subscript n_; Subscript mn_; // total size T* v_; T** row_; T* vm1_ ; // these point to the same data, but are 1-based T** rowm1_; // internal helper function to create the array // of row pointers void initialize(Subscript M, Subscript N) { mn_ = M*N; m_ = M; n_ = N; v_ = new T[mn_]; row_ = new T*[M]; rowm1_ = new T*[M]; assert(v_ != NULL); assert(row_ != NULL); assert(rowm1_ != NULL); T* p = v_; vm1_ = v_ - 1; for (Subscript i=0; i &A) { initialize(A.m_, A.n_); copy(A.v_); } /** Create a MxN matrix, with each element assigned to the value 0. @param M the number of rows @param N the number of columns @param value (optional default value: 0 if not specified. */ Matrix(Subscript M, Subscript N, const T& value = T(0)) { initialize(M,N); set(value); } /** Create an MxN matrix, filling in values (row-major order) from the list (C array) provided. @param M the number of rows @param N the number of columns @param v list (C array) of M*N values used to initialize matrix. */ Matrix(Subscript M, Subscript N, const T* v) { initialize(M,N); copy(v); } /** Create an MxN matrix, filling in values (row-major order) from a character string. @param M the number of rows @param N the number of columns @param s string of M*N values used to initialize matrix. */ Matrix(Subscript M, Subscript N, const char *s) { initialize(M,N); //std::istrstream ins(s); std::istringstream ins(s); Subscript i, j; for (i=0; i> row_[i][j]; } // destructor // ~Matrix() { destroy(); } /** Change size of matrix to MxN, reallocating memory if necessary.

NOTE: This operations occurs in place, i.e. when resizing to a new matrix, original matrix elements are NOT retained. Instead, one must explicit create a new matrix of this size and manually copy the elements, e.g.


				Matrix double B(M, N);

				int 	min_M = M < A.num_rows() ? M : A.num_rows();
				int 	min_N = N < A.num_cols() ? N : A.num_cols();
				for (int i=1; i<=min_M; i++)
					for (int j=1; j<=min_N; j++)
						B(i,j) = A(i,j);

				A.destroy();
				
@param M the number of rows of new size. @param N the number of columns of new size. */ Matrix& newsize(Subscript M, Subscript N) { if (num_rows() == M && num_cols() == N) return *this; destroy(); initialize(M,N); return *this; } /** Assign (copy) one matrix to another, e.g. A=B. The contents of A are lost, and a new copy of B is created. @param B to matrix to be copied. */ Matrix& operator=(const Matrix &B) { if (v_ == B.v_) return *this; if (m_ == B.m_ && n_ == B.n_) // no need to re-alloc copy(B.v_); else { destroy(); initialize(B.m_, B.n_); copy(B.v_); } return *this; } Matrix& operator=(const T& scalar) { set(scalar); return *this; } Subscript dim(Subscript d) const { #ifdef TNT_BOUNDS_CHECK assert( d >= 1); assert( d <= 2); #endif return (d==1) ? m_ : ((d==2) ? n_ : 0); } Subscript num_rows() const { return m_; } Subscript num_cols() const { return n_; } inline T* operator[](Subscript i) { #ifdef TNT_BOUNDS_CHECK assert(0<=i); assert(i < m_) ; #endif return row_[i]; } inline const T* operator[](Subscript i) const { #ifdef TNT_BOUNDS_CHECK assert(0<=i); assert(i < m_) ; #endif return row_[i]; } inline reference operator()(Subscript i) { #ifdef TNT_BOUNDS_CHECK assert(1<=i); assert(i <= mn_) ; #endif return vm1_[i]; } inline const_reference operator()(Subscript i) const { #ifdef TNT_BOUNDS_CHECK assert(1<=i); assert(i <= mn_) ; #endif return vm1_[i]; } inline reference operator()(Subscript i, Subscript j) { #ifdef TNT_BOUNDS_CHECK assert(1<=i); assert(i <= m_) ; assert(1<=j); assert(j <= n_); #endif return rowm1_[i][j]; } inline const_reference operator() (Subscript i, Subscript j) const { #ifdef TNT_BOUNDS_CHECK assert(1<=i); assert(i <= m_) ; assert(1<=j); assert(j <= n_); #endif return rowm1_[i][j]; } Vector diag() const { Subscript N = n_ < m_ ? n_ : m_; Vector d(N); for (int i=0; i upper_triangular() const; Matrix lower_triangular() const; /* basic computations */ }; /* *************************** I/O ********************************/ template std::ostream& operator<<(std::ostream &s, const Matrix &A) { Subscript M=A.num_rows(); Subscript N=A.num_cols(); s << M << " " << N << "\n"; for (Subscript i=0; i std::istream& operator>>(std::istream &s, Matrix &A) { Subscript M, N; s >> M >> N; if ( !(M == A.num_rows() && N == A.num_cols() )) { A.newsize(M,N); } for (Subscript i=0; i> A[i][j]; } return s; } // *******************[ basic matrix algorithms ]*************************** /** Matrix-Matrix multiplication: C = A * B.

This is an optimizied (trinary) version of matrix multiply, where the destination matrix has already been allocated. @param A matrix of size M x N. @param B matrix of size N x K. @param C the result A*B, of size M x K. @return a reference to C, after multiplication. */ template Matrix & mult(Matrix& C, const Matrix &A, const Matrix &B) { #ifdef TNT_BOUNDS_CHECK assert(A.num_cols() == B.num_rows()); #endif Subscript M = A.num_rows(); Subscript N = A.num_cols(); Subscript K = B.num_cols(); #ifdef TNT_BOUNDS_CHECK assert(C.num_rows() == M); assert(C.num_cols() == K); #endif T sum; const T* row_i; const T* col_k; for (Subscript i=0; i inline Matrix mult(const Matrix &A, const Matrix &B) { #ifdef TNT_BOUNDS_CHECK assert(A.num_cols() == B.num_rows()); #endif Subscript M = A.num_rows(); Subscript K = B.num_cols(); Matrix tmp(M,K); mult(tmp, A, B); // tmp = A*B return tmp; } /** Matrix/matrix multiplication: compute A * B. @param A matrix: left side operand (size M x N). @param B matrix: right side operand (size N x K). @return A*B (a new matirx of size M x K). */ template inline Matrix operator*(const Matrix &A, const Matrix &B) { return mult(A,B); } /** Matrix/vector multiplication: compute A * b. @param A matrix: left side operand (number of columns of A, must match the number of elements in b.) @param b vector: right side operand. @return A*b (a new vector of size M.) */ template inline Vector mult(const Matrix &A, const Vector &b) { #ifdef TNT_BOUNDS_CHECK assert(A.num_cols() == b.dim()); #endif Subscript M = A.num_rows(); Subscript N = A.num_cols(); Vector tmp(M); T sum; for (Subscript i=0; i inline Vector operator*(const Matrix &A, const Vector &b) { return mult(A,b); } /** Matrix scaling: multiply each element of A by scalar s.

NOTE: this creates a new copy of A. To scale "in place", use *= or mult_eq(). @param A matrix: to be scaled. @param s scalar: multiplier. @return s*A, a new matrix with same size of A. */ template inline Matrix mult(const T& s, const Matrix &A) { Subscript M = A.num_rows(); Subscript N = A.num_cols(); Matrix R(M,N); for (int i=0; i Same as mult(A,s), as this is a commutative operation.

NOTE: this creates a new copy of A. To scale "in place", use *= or mult_eq(). @param A matrix: to be scaled. @param s scalar: multiplier. @return s*A, a new matrix with same size of A. */ template inline Matrix mult(const Matrix &A, const T& s) { return mult(s, A); } /** Matrix scale in-place, i.e. compute A *= s, where each element of A is multiplied (scaled) by the value s.

NOTE: this creates a new copy of A. To scale "in place", use *= or mult_eq(). @param A matrix: to be scaled. @param s scalar: multiplier. @return A, after scaling. */ template inline Matrix mult_eq(const T& s, Matrix &A) { Subscript M = A.num_rows(); Subscript N = A.num_cols(); for (int i=0; i inline Matrix mult_eq(Matrix &A, const T&a) { return mult_eq(a, A); } /** Matrix-Matrix tranpose multiplication, i.e. compute tranpose(A)*B.

NOTE: this is more efficient than computing the tranpose(A) explicitly, and then multiplying, as the tranpose of A is never really constructed. @param A matrix: size M x N. @param B matrix: size M x K. @return a new matrix of size N x K. */ template inline Matrix transpose_mult(const Matrix &A, const Matrix &B) { #ifdef TNT_BOUNDS_CHECK assert(A.num_rows() == B.num_rows()); #endif Subscript M = A.num_cols(); Subscript N = A.num_rows(); Subscript K = B.num_cols(); Matrix tmp(M,K); T sum; for (Subscript i=0; i NOTE: this is more efficient than computing the tranpose(A) explicitly, and then multiplying, as the tranpose of A is not explicitly constructed. @param A Matrix: size M x N. @param b Vector: size M. @return a new vector of size N. */ template inline Vector transpose_mult(const Matrix &A, const Vector &b) { #ifdef TNT_BOUNDS_CHECK assert(A.num_rows() == b.dim()); #endif Subscript M = A.num_cols(); Subscript N = A.num_rows(); Vector tmp(M); for (Subscript i=0; i Matrix add(const Matrix &A, const Matrix &B) { Subscript M = A.num_rows(); Subscript N = A.num_cols(); assert(M==B.num_rows()); assert(N==B.num_cols()); Matrix tmp(M,N); Subscript i,j; for (i=0; i NOTE: this is shorthand notation for add(A,B). @param A matrix of size M x N. @param B matrix of size M x N. @param the sum A+B. */ template inline Matrix operator+(const Matrix &A, const Matrix &B) { return add(A,B); } /** Matrix addition, in place : compute A = A + B. @param A matrix of size M x N. @param B matrix of size M x N. @param the sum A+B. */ template Matrix& add_eq(Matrix &A, const Matrix &B) { Subscript M = A.num_rows(); Subscript N = A.num_cols(); assert(M==B.num_rows()); assert(N==B.num_cols()); Matrix tmp(M,N); Subscript i,j; for (i=0; i NOTE: this is shorthand notation for add_eq(A,B). @param A matrix of size M x N. @param B matrix of size M x N. @return the sum A+B. */ template inline Matrix operator+=(Matrix &A, const Matrix &B) { return add_eq(A,B); } /** Matrix subtraction : compute A - B. @param A matrix of size M x N. @param B matrix of size M x N. @return the result A-B. */ template Matrix minus(const Matrix & A, const Matrix &B) { Subscript M = A.num_rows(); Subscript N = A.num_cols(); assert(M==B.num_rows()); assert(N==B.num_cols()); Matrix tmp(M,N); Subscript i,j; for (i=0; i This is shorthand notation for minus(A,B). @param A matrix of size M x N. @param B matrix of size M x N. @return the result A-B. */ template inline Matrix operator-(const Matrix &A, const Matrix &B) { return minus(A,B); } /** Matrix element-by-elment multiplication: for each (i,j) compute A(i,j) * B(i,j). @param A matrix of size M x N. @param B matrix of size M x N. @return new matrix, where each (i,j) is A(i,j) * B(i,j); */ template Matrix mult_element(const Matrix &A, const Matrix &B) { Subscript M = A.num_rows(); Subscript N = A.num_cols(); assert(M==B.num_rows()); assert(N==B.num_cols()); Matrix tmp(M,N); Subscript i,j; for (i=0; i Matrix& mult_element_eq(Matrix &A, const Matrix &B) { Subscript M = A.num_rows(); Subscript N = A.num_cols(); assert(M==B.num_rows()); assert(N==B.num_cols()); Subscript i,j; for (i=0; i $$ \\sqrt{ \\sum_{i=1}{N} \\sum_{j=1}{N} A_{i,j}^{2} } $$. . @param A the matrix to compute its Frobeinus norm. @return the Frobenius norm of A. */ template double norm(const Matrix &A) { Subscript M = A.num_rows(); Subscript N = A.num_cols(); T sum = 0.0; for (int i=1; i<=M; i++) for (int j=1; j<=N; j++) sum += A(i,j) * A(i,j); return sqrt(sum); } /** Matrix tranpose: return a new matrix B, where B(i,j) is A(j,i). @param A matrix MxN @return new matrix of size N x M, where each (i,j) is A(j,i). */ template Matrix transpose(const Matrix &A) { Subscript M = A.num_rows(); Subscript N = A.num_cols(); Matrix S(N,M); Subscript i, j; for (i=0; i Vector upper_triangular_solve(const Matrix &A, const Vector &b) { int n = A.num_rows() < A.num_cols() ? A.num_rows() : A.num_cols(); Vector x(b); for (int k=n; k >= 1; k--) { x(k) /= A(k,k); for (int i=1; i< k; i++ ) x(i) -= x(k) * A(i,k); } return x; } /** Solve the triangular system A_L *x=b, where A_L is the lower triangular portion (including the diagonal) of A. @param A a square matrix of size N x N. @param b the right-hand-side (solution vector) of size N. @return x, such that A_u * x = b. */ template Vector lower_triangular_solve(const Matrix &A, const Vector &b) { int n = A.num_rows() < A.num_cols() ? A.num_rows() : A.num_cols(); Vector x(b); for (int k=1; k <= n; k++) { x(k) /= A(k,k); for (int i=k+1; i<= n; i++ ) x(i) -= x(k) * A(i,k); } return x; } //} /* namespace TNT::Linear_Algebra; */ } /* namespace TNT */ #endif // TNT_MATRIX_H