dynare/mex/sources/libkorder/sylv/QuasiTriangular.hh

476 lines
12 KiB
C++
Raw Normal View History

/*
* Copyright © 2004-2011 Ondra Kamenik
* Copyright © 2019-2023 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 QUASI_TRIANGULAR_H
#define QUASI_TRIANGULAR_H
#include "KronVector.hh"
#include "SylvMatrix.hh"
#include "Vector.hh"
#include <list>
#include <memory>
class DiagonalBlock;
class Diagonal;
2017-05-16 16:30:27 +02:00
class DiagPair
{
private:
double* a1;
double* a2;
public:
DiagPair() = default;
DiagPair(double* aa1, double* aa2) : a1 {aa1}, a2 {aa2}
2017-05-16 16:30:27 +02:00
{
}
DiagPair(const DiagPair& p) = default;
DiagPair& operator=(const DiagPair& p) = default;
DiagPair&
2017-05-16 16:30:27 +02:00
operator=(double v)
{
*a1 = v;
*a2 = v;
return *this;
2017-05-16 16:30:27 +02:00
}
const double&
2017-05-16 16:30:27 +02:00
operator*() const
{
return *a1;
}
/* Here we must not define double& operator*(), since it wouldn't
rewrite both values, we use operator=() for this */
2017-05-16 16:30:27 +02:00
friend class Diagonal;
friend class DiagonalBlock;
};
/* Stores a diagonal block of a quasi-triangular real matrix:
either a 1×1 block, i.e. a real scalar, stored in α
α β
or a 2×2 block, stored as β α
*/
2017-05-16 16:30:27 +02:00
class DiagonalBlock
{
private:
int jbar; // Index of block in the diagonal
2017-05-16 16:30:27 +02:00
bool real;
DiagPair alpha;
double* beta1;
double* beta2;
public:
DiagonalBlock() = default;
DiagonalBlock(int jb, bool r, double* a1, double* a2, double* b1, double* b2) :
jbar {jb}, real {r}, alpha {a1, a2}, beta1 {b1}, beta2 {b2}
2017-05-16 16:30:27 +02:00
{
}
// Construct a complex 2×2 block
/* β₁ and β₂ will be deduced from pointers to α₁ and α₂ */
DiagonalBlock(int jb, double* a1, double* a2) :
jbar {jb}, real {false}, alpha {a1, a2}, beta1 {a2 - 1}, beta2 {a1 + 1}
2017-05-16 16:30:27 +02:00
{
}
// Construct a real 1×1 block
DiagonalBlock(int jb, double* a1) :
jbar {jb}, real {true}, alpha {a1, a1}, beta1 {nullptr}, beta2 {nullptr}
2017-05-16 16:30:27 +02:00
{
}
DiagonalBlock(const DiagonalBlock& b) = default;
DiagonalBlock& operator=(const DiagonalBlock& b) = default;
[[nodiscard]] int
2017-05-16 16:30:27 +02:00
getIndex() const
{
return jbar;
}
[[nodiscard]] bool
2017-05-16 16:30:27 +02:00
isReal() const
{
return real;
}
[[nodiscard]] const DiagPair&
2017-05-16 16:30:27 +02:00
getAlpha() const
{
return alpha;
}
DiagPair&
2017-05-16 16:30:27 +02:00
getAlpha()
{
return alpha;
}
[[nodiscard]] double&
2017-05-16 16:30:27 +02:00
getBeta1() const
{
return *beta1;
}
[[nodiscard]] double&
2017-05-16 16:30:27 +02:00
getBeta2() const
{
return *beta2;
}
// Returns determinant of this block (assuming it is 2×2)
[[nodiscard]] double getDeterminant() const;
// Returns −β₁β₂
[[nodiscard]] double getSBeta() const;
// Returns the modulus of the eigenvalue(s) contained in this block
[[nodiscard]] double getSize() const;
// Transforms this block into a real one
2017-05-16 16:30:27 +02:00
void setReal();
// Verifies that the block information is consistent with the matrix d (for debugging)
void checkBlock(const double* d, int d_size);
2017-05-16 16:30:27 +02:00
friend class Diagonal;
};
// Stores the diagonal blocks of a quasi-triangular real matrix
2017-05-16 16:30:27 +02:00
class Diagonal
{
public:
using const_diag_iter = std::list<DiagonalBlock>::const_iterator;
using diag_iter = std::list<DiagonalBlock>::iterator;
private:
int num_all {0}; // Total number of blocks
std::list<DiagonalBlock> blocks;
int num_real {0}; // Number of 1×1 (real) blocks
public:
Diagonal() = default;
// Construct the diagonal blocks of (quasi-triangular) matrix data
Diagonal(double* data, int d_size);
/* Construct the diagonal blocks of (quasi-triangular) matrix data,
assuming it has the same shape as d */
Diagonal(double* data, const Diagonal& d);
Diagonal(const Diagonal& d) = default;
Diagonal& operator=(const Diagonal& d) = default;
virtual ~Diagonal() = default;
// Returns number of 2×2 blocks on the diagonal
[[nodiscard]] int
2017-05-16 16:30:27 +02:00
getNumComplex() const
{
return num_all - num_real;
}
// Returns number of 1×1 blocks on the diagonal
[[nodiscard]] int
2017-05-16 16:30:27 +02:00
getNumReal() const
{
return num_real;
}
// Returns number of scalar elements on the diagonal
[[nodiscard]] int
2017-05-16 16:30:27 +02:00
getSize() const
{
return getNumReal() + 2 * getNumComplex();
2017-05-16 16:30:27 +02:00
}
// Returns total number of blocks on the diagonal
[[nodiscard]] int
2017-05-16 16:30:27 +02:00
getNumBlocks() const
{
return num_all;
}
void getEigenValues(Vector& eig) const;
2017-05-16 16:30:27 +02:00
void swapLogically(diag_iter it);
void checkConsistency(diag_iter it);
double getAverageSize(diag_iter start, diag_iter end);
diag_iter findClosestBlock(diag_iter start, diag_iter end, double a);
diag_iter findNextLargerBlock(diag_iter start, diag_iter end, double a);
void print() const;
2017-05-16 16:30:27 +02:00
diag_iter
begin()
{
return blocks.begin();
2017-05-16 16:30:27 +02:00
}
[[nodiscard]] const_diag_iter
2017-05-16 16:30:27 +02:00
begin() const
{
return blocks.begin();
2017-05-16 16:30:27 +02:00
}
diag_iter
end()
{
return blocks.end();
2017-05-16 16:30:27 +02:00
}
[[nodiscard]] const_diag_iter
2017-05-16 16:30:27 +02:00
end() const
{
return blocks.end();
2017-05-16 16:30:27 +02:00
}
2017-05-16 16:30:27 +02:00
/* redefine pointers as data start at p */
void changeBase(double* p);
private:
constexpr static double EPS = 1.0e-300;
/* Computes number of 2×2 diagonal blocks on the quasi-triangular matrix
represented by data (of size d_size×d_size) */
static int getNumComplex(const double* data, int d_size);
// Checks whether |p|<EPS
2017-05-16 16:30:27 +02:00
static bool isZero(double p);
};
2019-12-20 14:36:20 +01:00
template<class _TRef, class _TPtr>
2017-05-16 16:30:27 +02:00
struct _matrix_iter
{
using _Self = _matrix_iter<_TRef, _TPtr>;
2017-05-16 16:30:27 +02:00
int d_size;
bool real;
_TPtr ptr;
public:
2017-05-16 16:30:27 +02:00
_matrix_iter(_TPtr base, int ds, bool r)
{
ptr = base;
d_size = ds;
real = r;
2017-05-16 16:30:27 +02:00
}
virtual ~_matrix_iter() = default;
2017-05-16 16:30:27 +02:00
bool
operator==(const _Self& it) const
2017-05-16 16:30:27 +02:00
{
return ptr == it.ptr;
}
bool
operator!=(const _Self& it) const
2017-05-16 16:30:27 +02:00
{
return ptr != it.ptr;
}
_TRef
operator*() const
{
return *ptr;
}
_TRef
a() const
{
return *ptr;
}
virtual _Self& operator++() = 0;
};
2019-12-20 14:36:20 +01:00
template<class _TRef, class _TPtr>
2017-05-16 16:30:27 +02:00
class _column_iter : public _matrix_iter<_TRef, _TPtr>
{
using _Tparent = _matrix_iter<_TRef, _TPtr>;
using _Self = _column_iter<_TRef, _TPtr>;
2017-05-16 16:30:27 +02:00
int row;
public:
_column_iter(_TPtr base, int ds, bool r, int rw) :
_matrix_iter<_TRef, _TPtr>(base, ds, r), row(rw) {};
_Self&
operator++() override
2017-05-16 16:30:27 +02:00
{
_Tparent::ptr++;
row++;
return *this;
2017-05-16 16:30:27 +02:00
}
_TRef
b() const
{
if (_Tparent::real)
return *(_Tparent::ptr);
2017-05-16 16:30:27 +02:00
else
return *(_Tparent::ptr + _Tparent::d_size);
2017-05-16 16:30:27 +02:00
}
[[nodiscard]] int
2017-05-16 16:30:27 +02:00
getRow() const
{
return row;
}
};
2019-12-20 14:36:20 +01:00
template<class _TRef, class _TPtr>
2017-05-16 16:30:27 +02:00
class _row_iter : public _matrix_iter<_TRef, _TPtr>
{
using _Tparent = _matrix_iter<_TRef, _TPtr>;
using _Self = _row_iter<_TRef, _TPtr>;
2017-05-16 16:30:27 +02:00
int col;
public:
_row_iter(_TPtr base, int ds, bool r, int cl) :
_matrix_iter<_TRef, _TPtr>(base, ds, r), col(cl) {};
_Self&
operator++() override
2017-05-16 16:30:27 +02:00
{
_Tparent::ptr += _Tparent::d_size;
col++;
return *this;
2017-05-16 16:30:27 +02:00
}
virtual _TRef
b() const
{
if (_Tparent::real)
return *(_Tparent::ptr);
2017-05-16 16:30:27 +02:00
else
return *(_Tparent::ptr + 1);
2017-05-16 16:30:27 +02:00
}
[[nodiscard]] int
2017-05-16 16:30:27 +02:00
getCol() const
{
return col;
}
};
class SchurDecomp;
class SchurDecompZero;
/* Represents an upper quasi-triangular matrix.
All the elements are stored in the SqSylvMatrix super-class.
Additionally, a list of the diagonal blocks (1×1 or 2×2), is stored in the
diagonal member, in order to optimize some operations (where the matrix is
seen as an upper-triangular matrix, plus sub-diagonal elements of the 2×2
diagonal blocks) */
2017-05-16 16:30:27 +02:00
class QuasiTriangular : public SqSylvMatrix
{
public:
using const_col_iter = _column_iter<const double&, const double*>;
using col_iter = _column_iter<double&, double*>;
using const_row_iter = _row_iter<const double&, const double*>;
using row_iter = _row_iter<double&, double*>;
using const_diag_iter = Diagonal::const_diag_iter;
using diag_iter = Diagonal::diag_iter;
protected:
2017-05-16 16:30:27 +02:00
Diagonal diagonal;
public:
QuasiTriangular(const ConstVector& d, int d_size);
// Initializes with r·t
QuasiTriangular(double r, const QuasiTriangular& t);
// Initializes with r·t+r₂·t₂
QuasiTriangular(double r, const QuasiTriangular& t, double r2, const QuasiTriangular& t2);
// Initializes with t²
QuasiTriangular(const std::string& dummy, const QuasiTriangular& t);
explicit QuasiTriangular(const SchurDecomp& decomp);
explicit QuasiTriangular(const SchurDecompZero& decomp);
QuasiTriangular(const QuasiTriangular& t);
2019-12-20 14:36:20 +01:00
~QuasiTriangular() override = default;
[[nodiscard]] const Diagonal&
2017-05-16 16:30:27 +02:00
getDiagonal() const
{
return diagonal;
}
[[nodiscard]] int getNumOffdiagonal() const;
2017-05-16 16:30:27 +02:00
void swapDiagLogically(diag_iter it);
void checkDiagConsistency(diag_iter it);
double getAverageDiagSize(diag_iter start, diag_iter end);
diag_iter findClosestDiagBlock(diag_iter start, diag_iter end, double a);
diag_iter findNextLargerBlock(diag_iter start, diag_iter end, double a);
/* (I+this)·y = x, y→x */
virtual void solvePre(Vector& x, double& eig_min);
/* (I+thisᵀ)·y = x, y→x */
virtual void solvePreTrans(Vector& x, double& eig_min);
/* (I+this)·x = b */
virtual void solve(Vector& x, const ConstVector& b, double& eig_min);
/* (I+thisᵀ)·x = b */
virtual void solveTrans(Vector& x, const ConstVector& b, double& eig_min);
/* x = this·b */
virtual void multVec(Vector& x, const ConstVector& b) const;
/* x = thisᵀ·b */
virtual void multVecTrans(Vector& x, const ConstVector& b) const;
/* x = x + this·b */
virtual void multaVec(Vector& x, const ConstVector& b) const;
/* x = x + thisᵀ·b */
virtual void multaVecTrans(Vector& x, const ConstVector& b) const;
/* x = (this⊗I)·x */
virtual void multKron(KronVector& x) const;
/* x = (thisᵀ⊗I)·x */
virtual void multKronTrans(KronVector& x) const;
/* A = this·A */
virtual void multLeftOther(GeneralMatrix& a) const;
/* A = thisᵀ·A */
virtual void multLeftOtherTrans(GeneralMatrix& a) const;
[[nodiscard]] const_diag_iter
2017-05-16 16:30:27 +02:00
diag_begin() const
{
return diagonal.begin();
}
diag_iter
diag_begin()
{
return diagonal.begin();
}
[[nodiscard]] const_diag_iter
2017-05-16 16:30:27 +02:00
diag_end() const
{
return diagonal.end();
}
diag_iter
diag_end()
{
return diagonal.end();
}
2017-05-16 16:30:27 +02:00
/* iterators for off diagonal elements */
[[nodiscard]] virtual const_col_iter col_begin(const DiagonalBlock& b) const;
virtual col_iter col_begin(const DiagonalBlock& b);
[[nodiscard]] virtual const_row_iter row_begin(const DiagonalBlock& b) const;
virtual row_iter row_begin(const DiagonalBlock& b);
[[nodiscard]] virtual const_col_iter col_end(const DiagonalBlock& b) const;
virtual col_iter col_end(const DiagonalBlock& b);
[[nodiscard]] virtual const_row_iter row_end(const DiagonalBlock& b) const;
virtual row_iter row_end(const DiagonalBlock& b);
[[nodiscard]] virtual std::unique_ptr<QuasiTriangular>
2017-05-16 16:30:27 +02:00
clone() const
{
return std::make_unique<QuasiTriangular>(*this);
2017-05-16 16:30:27 +02:00
}
// Returns this²
[[nodiscard]] virtual std::unique_ptr<QuasiTriangular>
square() const
2017-05-16 16:30:27 +02:00
{
return std::make_unique<QuasiTriangular>("square", *this);
2017-05-16 16:30:27 +02:00
}
// Returns r·this
[[nodiscard]] virtual std::unique_ptr<QuasiTriangular>
scale(double r) const
2017-05-16 16:30:27 +02:00
{
return std::make_unique<QuasiTriangular>(r, *this);
2017-05-16 16:30:27 +02:00
}
// Returns r·this + r₂·t₂
[[nodiscard]] virtual std::unique_ptr<QuasiTriangular>
linearlyCombine(double r, double r2, const QuasiTriangular& t2) const
2017-05-16 16:30:27 +02:00
{
return std::make_unique<QuasiTriangular>(r, *this, r2, t2);
2017-05-16 16:30:27 +02:00
}
protected:
// this = r·t
void setMatrix(double r, const QuasiTriangular& t);
// this = this + r·t
void addMatrix(double r, const QuasiTriangular& t);
private:
// this = this + I
2017-05-16 16:30:27 +02:00
void addUnit();
/* x = x + (this⊗I)·b */
void multaKron(KronVector& x, const ConstKronVector& b) const;
/* x = x + (thisᵀ⊗I)·b */
void multaKronTrans(KronVector& x, const ConstKronVector& b) const;
2017-05-16 16:30:27 +02:00
/* hide noneffective implementations of parents */
void multsVec(Vector& x, const ConstVector& d) const;
void multsVecTrans(Vector& x, const ConstVector& d) const;
};
#endif /* QUASI_TRIANGULAR_H */