dynare/dynare++/sylv/cc/GeneralMatrix.hh

622 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef GENERAL_MATRIX_H
#define GENERAL_MATRIX_H
#include "Vector.hh"
#include "SylvException.hh"
#include <algorithm>
#include <memory>
#include <utility>
#include <string>
template<class T>
class TransposedMatrix
{
friend class GeneralMatrix;
template<class T2>
friend GeneralMatrix operator*(const ConstGeneralMatrix &a, const TransposedMatrix<T2> &b);
template<class T2>
friend GeneralMatrix operator*(const TransposedMatrix<T2> &a, const ConstGeneralMatrix &b);
template<class T1, class T2>
friend GeneralMatrix operator*(const TransposedMatrix<T1> &a, const TransposedMatrix<T2> &b);
private:
T &orig;
public:
TransposedMatrix(T &orig_arg) : orig{orig_arg}
{
};
};
// Syntactic sugar for representing a transposed matrix
template<class T>
TransposedMatrix<T>
transpose(T &m)
{
return TransposedMatrix<T>(m);
}
class GeneralMatrix;
class ConstGeneralMatrix
{
friend class GeneralMatrix;
friend GeneralMatrix operator*(const ConstGeneralMatrix &a, const ConstGeneralMatrix &b);
template<class T>
friend GeneralMatrix operator*(const ConstGeneralMatrix &a, const TransposedMatrix<T> &b);
template<class T>
friend GeneralMatrix operator*(const TransposedMatrix<T> &a, const ConstGeneralMatrix &b);
template<class T1, class T2>
friend GeneralMatrix operator*(const TransposedMatrix<T1> &a, const TransposedMatrix<T2> &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<int>(mxGetM(p))}, cols{static_cast<int>(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<class T>
friend GeneralMatrix operator*(const ConstGeneralMatrix &a, const TransposedMatrix<T> &b);
template<class T>
friend GeneralMatrix operator*(const TransposedMatrix<T> &a, const ConstGeneralMatrix &b);
template<class T1, class T2>
friend GeneralMatrix operator*(const TransposedMatrix<T1> &a, const TransposedMatrix<T2> &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<class T>
explicit GeneralMatrix(const TransposedMatrix<T> &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<int>(mxGetM(p))}, cols{static_cast<int>(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<class T>
GeneralMatrix
operator*(const ConstGeneralMatrix &a, const TransposedMatrix<T> &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<class T>
GeneralMatrix
operator*(const TransposedMatrix<T> &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<class T1, class T2>
GeneralMatrix
operator*(const TransposedMatrix<T1> &a, const TransposedMatrix<T2> &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<int>(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 */