/* * Copyright © 2004-2011 Ondra Kamenik * Copyright © 2019 Dynare Team * * This file is part of Dynare. * * Dynare is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Dynare is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Dynare. If not, see . */ #ifndef GENERAL_MATRIX_H #define GENERAL_MATRIX_H #include "Vector.hh" #include "SylvException.hh" #include #include #include #include template class TransposedMatrix { friend class GeneralMatrix; template friend GeneralMatrix operator*(const ConstGeneralMatrix &a, const TransposedMatrix &b); template friend GeneralMatrix operator*(const TransposedMatrix &a, const ConstGeneralMatrix &b); template friend GeneralMatrix operator*(const TransposedMatrix &a, const TransposedMatrix &b); private: T &orig; public: TransposedMatrix(T &orig_arg) : orig{orig_arg} { }; }; // Syntactic sugar for representing a transposed matrix template TransposedMatrix transpose(T &m) { return TransposedMatrix(m); } class GeneralMatrix; class ConstGeneralMatrix { friend class GeneralMatrix; friend GeneralMatrix operator*(const ConstGeneralMatrix &a, const ConstGeneralMatrix &b); template friend GeneralMatrix operator*(const ConstGeneralMatrix &a, const TransposedMatrix &b); template friend GeneralMatrix operator*(const TransposedMatrix &a, const ConstGeneralMatrix &b); template friend GeneralMatrix operator*(const TransposedMatrix &a, const TransposedMatrix &b); protected: ConstVector data; // Has unit-stride int rows; int cols; int ld; public: ConstGeneralMatrix(ConstVector d, int m, int n) : data(std::move(d)), rows(m), cols(n), ld(m) { if (data.skip() > 1) throw SYLV_MES_EXCEPTION("Vector must have unit-stride"); if (data.length() < m*n) throw SYLV_MES_EXCEPTION("Vector is too small"); } ConstGeneralMatrix(const ConstGeneralMatrix &m) = default; ConstGeneralMatrix(ConstGeneralMatrix &&m) = default; // Implicit conversion from GeneralMatrix is ok, since it's cheap ConstGeneralMatrix(const GeneralMatrix &m); // Create submatrix (with data sharing) ConstGeneralMatrix(const GeneralMatrix &m, int i, int j, int nrows, int ncols); // Create submatrix (with data sharing) ConstGeneralMatrix(const ConstGeneralMatrix &m, int i, int j, int nrows, int ncols); #if defined(MATLAB_MEX_FILE) || defined(OCTAVE_MEX_FILE) explicit ConstGeneralMatrix(const mxArray *p) : data(p), rows{static_cast(mxGetM(p))}, cols{static_cast(mxGetN(p))}, ld{rows} { } #endif virtual ~ConstGeneralMatrix() = default; ConstGeneralMatrix &operator=(const ConstGeneralMatrix &v) = delete; ConstGeneralMatrix &operator=(ConstGeneralMatrix &&v) = delete; const double & get(int i, int j) const { return data[j*ld+i]; } int nrows() const { return rows; } int ncols() const { return cols; } int getLD() const { return ld; } const double * base() const { return data.base(); } const ConstVector & getData() const { return data; } ConstVector getRow(int row) const; ConstVector getCol(int col) const; double getNormInf() const; double getNorm1() const; /* x = scalar(a)*x + scalar(b)*this*d */ void multVec(double a, Vector &x, double b, const ConstVector &d) const; /* x = scalar(a)*x + scalar(b)*this'*d */ void multVecTrans(double a, Vector &x, double b, const ConstVector &d) const; /* x = x + this*d */ void multaVec(Vector &x, const ConstVector &d) const { multVec(1.0, x, 1.0, d); } /* x = x + this'*d */ void multaVecTrans(Vector &x, const ConstVector &d) const { multVecTrans(1.0, x, 1.0, d); } /* x = x - this*d */ void multsVec(Vector &x, const ConstVector &d) const { multVec(1.0, x, -1.0, d); } /* x = x - this'*d */ void multsVecTrans(Vector &x, const ConstVector &d) const { multVecTrans(1.0, x, -1.0, d); } /* m = inv(this)*m */ void multInvLeft(GeneralMatrix &m) const; /* m = inv(this')*m */ void multInvLeftTrans(GeneralMatrix &m) const; /* d = inv(this)*d */ void multInvLeft(Vector &d) const; /* d = inv(this')*d */ void multInvLeftTrans(Vector &d) const; bool isFinite() const; /** Returns true of the matrix is exactly zero. */ bool isZero() const; virtual void print() const; protected: void multInvLeft(const std::string &trans, int mrows, int mcols, int mld, double *d) const; }; class GeneralMatrix { friend class ConstGeneralMatrix; friend GeneralMatrix operator*(const ConstGeneralMatrix &a, const ConstGeneralMatrix &b); template friend GeneralMatrix operator*(const ConstGeneralMatrix &a, const TransposedMatrix &b); template friend GeneralMatrix operator*(const TransposedMatrix &a, const ConstGeneralMatrix &b); template friend GeneralMatrix operator*(const TransposedMatrix &a, const TransposedMatrix &b); protected: Vector data; // Has unit-stride int rows; int cols; int ld; public: GeneralMatrix(int m, int n) : data(m*n), rows(m), cols(n), ld(m) { } GeneralMatrix(Vector d, int m, int n) : data(std::move(d)), rows(m), cols(n), ld(m) { if (data.skip() > 1) throw SYLV_MES_EXCEPTION("Vector must have unit-stride"); if (data.length() < m*n) throw SYLV_MES_EXCEPTION("Vector is too small"); } /* The copies will have ld==rows, for memory efficiency (hence we do not use the default copy constructor) */ GeneralMatrix(const GeneralMatrix &m); // We don't want implict conversion from ConstGeneralMatrix, since it's expensive explicit GeneralMatrix(const ConstGeneralMatrix &m); GeneralMatrix(GeneralMatrix &&m) = default; template explicit GeneralMatrix(const TransposedMatrix &m) : data(m.orig.rows*m.orig.cols), rows(m.orig.cols), cols(m.orig.rows), ld(rows) { for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) get(i, j) = m.orig.get(j, i); } // Create submatrix (with data copy) GeneralMatrix(const GeneralMatrix &m, int i, int j, int nrows, int ncols); // Create submatrix (with data sharing) GeneralMatrix(GeneralMatrix &m, int i, int j, int nrows, int ncols); #if defined(MATLAB_MEX_FILE) || defined(OCTAVE_MEX_FILE) explicit GeneralMatrix(mxArray *p) : data(p), rows{static_cast(mxGetM(p))}, cols{static_cast(mxGetN(p))}, ld{rows} { } #endif virtual ~GeneralMatrix() = default; GeneralMatrix &operator=(const GeneralMatrix &m) = default; GeneralMatrix &operator=(GeneralMatrix &&m) = default; GeneralMatrix &operator=(const ConstGeneralMatrix &m); const double & get(int i, int j) const { return data[j*ld+i]; } double & get(int i, int j) { return data[j*ld+i]; } int nrows() const { return rows; } int ncols() const { return cols; } int getLD() const { return ld; } double * base() { return data.base(); } const double * base() const { return data.base(); } Vector & getData() { return data; } ConstVector getData() const { return data; } Vector getRow(int row); Vector getCol(int col); ConstVector getRow(int row) const; ConstVector getCol(int col) const; double getNormInf() const { return ConstGeneralMatrix(*this).getNormInf(); } double getNorm1() const { return ConstGeneralMatrix(*this).getNorm1(); } /* place matrix m to the position (i,j) */ void place(const ConstGeneralMatrix &m, int i, int j); void place(const GeneralMatrix &m, int i, int j) { place(ConstGeneralMatrix(m), i, j); } // this = a·b void mult(const ConstGeneralMatrix &a, const ConstGeneralMatrix &b); void mult(const GeneralMatrix &a, const GeneralMatrix &b) { mult(ConstGeneralMatrix(a), ConstGeneralMatrix(b)); } // this = this + scalar·a·b void multAndAdd(const ConstGeneralMatrix &a, const ConstGeneralMatrix &b, double mult = 1.0); void multAndAdd(const GeneralMatrix &a, const GeneralMatrix &b, double mult = 1.0) { multAndAdd(ConstGeneralMatrix(a), ConstGeneralMatrix(b), mult); } // this = this + scalar·a·bᵀ void multAndAdd(const ConstGeneralMatrix &a, const ConstGeneralMatrix &b, const std::string &dum, double mult = 1.0); void multAndAdd(const GeneralMatrix &a, const GeneralMatrix &b, const std::string &dum, double mult = 1.0) { multAndAdd(ConstGeneralMatrix(a), ConstGeneralMatrix(b), dum, mult); } // this = this + scalar·aᵀ·b void multAndAdd(const ConstGeneralMatrix &a, const std::string &dum, const ConstGeneralMatrix &b, double mult = 1.0); void multAndAdd(const GeneralMatrix &a, const std::string &dum, const GeneralMatrix &b, double mult = 1.0) { multAndAdd(ConstGeneralMatrix(a), dum, ConstGeneralMatrix(b), mult); } // this = this + scalar·aᵀ·bᵀ void multAndAdd(const ConstGeneralMatrix &a, const std::string &dum1, const ConstGeneralMatrix &b, const std::string &dum2, double mult = 1.0); void multAndAdd(const GeneralMatrix &a, const std::string &dum1, const GeneralMatrix &b, const std::string &dum2, double mult = 1.0) { multAndAdd(ConstGeneralMatrix(a), dum1, ConstGeneralMatrix(b), dum2, mult); } // this = this + scalar·a·aᵀ void addOuter(const ConstVector &a, double mult = 1.0); void addOuter(const Vector &a, double mult = 1.0) { addOuter(ConstVector(a), mult); } // this = this·m void multRight(const ConstGeneralMatrix &m); void multRight(const GeneralMatrix &m) { multRight(ConstGeneralMatrix(m)); } // this = m·this void multLeft(const ConstGeneralMatrix &m); void multLeft(const GeneralMatrix &m) { multLeft(ConstGeneralMatrix(m)); } // this = this·mᵀ void multRightTrans(const ConstGeneralMatrix &m); void multRightTrans(const GeneralMatrix &m) { multRightTrans(ConstGeneralMatrix(m)); } // this = mᵀ·this void multLeftTrans(const ConstGeneralMatrix &m); void multLeftTrans(const GeneralMatrix &m) { multLeftTrans(ConstGeneralMatrix(m)); } // x = scalar(a)·x + scalar(b)·this·d void multVec(double a, Vector &x, double b, const ConstVector &d) const { ConstGeneralMatrix(*this).multVec(a, x, b, d); } // x = scalar(a)·x + scalar(b)·thisᵀ·d void multVecTrans(double a, Vector &x, double b, const ConstVector &d) const { ConstGeneralMatrix(*this).multVecTrans(a, x, b, d); } // x = x + this·d void multaVec(Vector &x, const ConstVector &d) const { ConstGeneralMatrix(*this).multaVec(x, d); } // x = x + thisᵀ·d */ void multaVecTrans(Vector &x, const ConstVector &d) const { ConstGeneralMatrix(*this).multaVecTrans(x, d); } // x = x - this·d void multsVec(Vector &x, const ConstVector &d) const { ConstGeneralMatrix(*this).multsVec(x, d); } // x = x - thisᵀ·d void multsVecTrans(Vector &x, const ConstVector &d) const { ConstGeneralMatrix(*this).multsVecTrans(x, d); } // this = zero void zeros(); // this = unit (on main diagonal) void unit(); // this = NaN void nans(); // this = ∞ void infs(); // this = scalar·this void mult(double a); // this = this + scalar·m void add(double a, const ConstGeneralMatrix &m); void add(double a, const GeneralMatrix &m) { add(a, ConstGeneralMatrix(m)); } // this = this + scalar·mᵀ void add(double a, const ConstGeneralMatrix &m, const std::string &dum); void add(double a, const GeneralMatrix &m, const std::string &dum) { add(a, ConstGeneralMatrix(m), dum); } bool isFinite() const { return (ConstGeneralMatrix(*this)).isFinite(); } bool isZero() const { return (ConstGeneralMatrix(*this)).isZero(); } virtual void print() const { ConstGeneralMatrix(*this).print(); } private: void copy(const ConstGeneralMatrix &m, int ioff = 0, int joff = 0); void copy(const GeneralMatrix &m, int ioff = 0, int joff = 0) { copy(ConstGeneralMatrix(m), ioff, joff); } void gemm(const std::string &transa, const ConstGeneralMatrix &a, const std::string &transb, const ConstGeneralMatrix &b, double alpha, double beta); void gemm(const std::string &transa, const GeneralMatrix &a, const std::string &transb, const GeneralMatrix &b, double alpha, double beta) { gemm(transa, ConstGeneralMatrix(a), transb, ConstGeneralMatrix(b), alpha, beta); } /* this = this * op(m) (without whole copy of this) */ void gemm_partial_right(const std::string &trans, const ConstGeneralMatrix &m, double alpha, double beta); void gemm_partial_right(const std::string &trans, const GeneralMatrix &m, double alpha, double beta) { gemm_partial_right(trans, ConstGeneralMatrix(m), alpha, beta); } // this = op(m)·this (without whole copy of ‘this’) void gemm_partial_left(const std::string &trans, const ConstGeneralMatrix &m, double alpha, double beta); void gemm_partial_left(const std::string &trans, const GeneralMatrix &m, double alpha, double beta) { gemm_partial_left(trans, ConstGeneralMatrix(m), alpha, beta); } /* number of rows/columns for copy used in gemm_partial_* */ static constexpr int md_length = 23; }; // Computes a·b inline GeneralMatrix operator*(const ConstGeneralMatrix &a, const ConstGeneralMatrix &b) { GeneralMatrix m(a.rows, b.cols); m.gemm("N", a, "N", b, 1.0, 0.0); return m; } // Computes a·bᵀ template GeneralMatrix operator*(const ConstGeneralMatrix &a, const TransposedMatrix &b) { GeneralMatrix m(a.rows, b.orig.rows); m.gemm("N", a, "T", b.orig, 1.0, 0.0); return m; } // Computes aᵀ·b template GeneralMatrix operator*(const TransposedMatrix &a, const ConstGeneralMatrix &b) { GeneralMatrix m(a.orig.cols, b.cols); m.gemm("T", a.orig, "N", b, 1.0, 0.0); return m; } // Computes aᵀ·bᵀ template GeneralMatrix operator*(const TransposedMatrix &a, const TransposedMatrix &b) { GeneralMatrix m(a.orig.cols, b.orig.rows); m.gemm("T", a.orig, "T", b.orig, 1.0, 0.0); return m; } class SVDDecomp { protected: // Minimum of number of rows and columns of the decomposed matrix const int minmn; // Singular values Vector sigma; // Orthogonal matrix U GeneralMatrix U; // Orthogonal matrix Vᵀ GeneralMatrix VT; // Convered flag bool conv; public: SVDDecomp(const GeneralMatrix &A) : minmn(std::min(A.nrows(), A.ncols())), sigma(minmn), U(A.nrows(), A.nrows()), VT(A.ncols(), A.ncols()), conv(false) { construct(A); } const GeneralMatrix & getU() const { return U; } const GeneralMatrix & getVT() const { return VT; } void solve(const ConstGeneralMatrix &B, GeneralMatrix &X) const; void solve(const ConstVector &b, Vector &x) const { GeneralMatrix xmat(x, x.length(), 1); solve(ConstGeneralMatrix(b, b.length(), 1), xmat); } private: void construct(const GeneralMatrix &A); }; #endif /* GENERAL_MATRIX_H */