/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2020-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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.
OpenFOAM 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 OpenFOAM. If not, see .
Application
Test-QRMatrix
Description
Tests for \c QRMatrix constructors, and member functions
using \c doubleScalar and \c complex base types.
\*---------------------------------------------------------------------------*/
#include "MatrixTools.H"
#include "QRMatrix.H"
#include "complex.H"
#include "IOmanip.H"
#include "TestTools.H"
#define PRINTALL false
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Verify if Q is a unitary matrix for rectangular matrices
template
void verify_Q(const RectangularMatrix& Q)
{
// non-square unitary matrices are not possible - do nothing
}
// Verify if Q is a unitary matrix for square matrices
template
void verify_Q
(
const SquareMatrix& Q
)
{
// mathworld.wolfram.com/UnitaryMatrix.html (Retrieved:18-06-19)
const SquareMatrix QTQ(Q&Q);
const SquareMatrix QQT(Q^Q);
SquareMatrix IMatrix(Q.sizes(), Zero);
for (label i = 0; i < IMatrix.n(); ++i)
{
IMatrix(i,i) = pTraits::one;
}
cmp("# Q.T()*Q = I: ", flt(QTQ), flt(IMatrix), getTol(Type(0)));
cmp("# Q*Q.T() = I: ", flt(QQT), flt(IMatrix), getTol(Type(0)));
cmp("# QTQ = QQT: ", flt(QTQ), flt(QQT), getTol(Type(0)));
}
// Verify if R is an upper-triangular matrix
template
void verify_R
(
const MatrixType& R
)
{
DynamicList ltriR;
label k = 0;
// mathworld.wolfram.com/UpperTriangularMatrix.html (Retrieved:18-06-19)
for (label i = 0; i < R.m(); ++i)
{
for (label j = 0; j < R.n(); ++j)
{
if (j < i)
{
ltriR.append(mag(R(i, j)));
++k;
}
}
}
const List ltri(k, Zero);
cmp("# R(i, j) = 0 for i > j: ", ltri, ltriR);
}
// Verify if R is an upper-triangular matrix with diag elems non-increasing
template
void verify_R_pivoting
(
const MatrixType& R
)
{
// w.wiki/54m (Retrieved:20-06-19)
const List diag0(mag(R.diag()));
List diag1(diag0);
Foam::sort(diag1, std::greater());
cmp
(
"# Diag elems of R non-increasing: |R11| >= |R22| >= ... >= |Rnn|: ",
diag0,
diag1
);
}
// Verify if the given matrix can be reconstructed by Q and R matrices
template
void verify_QR
(
const MatrixType& A,
const MatrixType& Q,
const MatrixType& R
)
{
// mathworld.wolfram.com/QRDecomposition.html (Retrieved:18-06-19)
const MatrixType AReconstruct(Q*R);
cmp("# Q*R = A: ", flt(A), flt(AReconstruct));
}
// Verify if the given matrix can be reconstructed by Q, R and P matrices
template
void verify_QR_pivoting
(
const MatrixType& A,
const MatrixType& Q,
const MatrixType& R,
const SquareMatrix& P
)
{
// w.wiki/54m (Retrieved:20-06-19)
const MatrixType AReconstruct(Q*R);
const MatrixType AP(A*P);
cmp("# Q*R = A*P: ", flt(AReconstruct), flt(AP));
}
// Verify the given QR decomposition
template
void verify
(
const QRMatrix& QRM,
const MatrixType& A
)
{
const MatrixType& Q = QRM.Q();
const MatrixType& R = QRM.R();
#if PRINTALL
InfoNumPyFormat(A, "A");
InfoNumPyFormat(Q, "Q");
InfoNumPyFormat(R, "R");
#endif
verify_Q(Q);
verify_R(R);
verify_QR(A, Q, R);
Info<< endl;
}
// Verify the given QR decomposition with column pivoting
template
void verify_pivoting
(
const QRMatrix& QRM,
const MatrixType& A
)
{
const MatrixType& Q = QRM.Q();
const MatrixType& R = QRM.R();
const SquareMatrix& P = QRM.P();
#if PRINTALL
InfoNumPyFormat(A, "A");
InfoNumPyFormat(Q, "Q");
InfoNumPyFormat(R, "R");
InfoNumPyFormat(P, "P");
#endif
verify_Q(Q);
verify_R(R);
verify_R_pivoting(R);
verify_QR_pivoting(A, Q, R, P);
Info<< endl;
}
// Verify various QR decompositions
template
void verify_decomposition
(
const MatrixType& A
)
{
Info<< "## Verify various QR decompositions" << nl << endl;
{
Info<< "# modes::FULL" << nl;
QRMatrix QRM
(
A,
QRMatrix::modes::FULL
);
verify(QRM, A);
}
{
Info<< "# modes::ECONOMY" << nl;
QRMatrix QRM
(
A,
QRMatrix::modes::ECONOMY
);
verify(QRM, A);
}
{
Info<< "# modes::FULL, outputs::BOTH_QR, column-pivoting" << nl;
QRMatrix QRM
(
A,
QRMatrix::modes::FULL,
QRMatrix::outputs::BOTH_QR,
QRMatrix::pivoting::TRUE
);
verify_pivoting(QRM, A);
}
{
Info<< "# modes::ECONOMY, outputs::BOTH_QR, column-pivoting" << nl;
QRMatrix QRM
(
A,
QRMatrix::modes::ECONOMY,
QRMatrix::outputs::BOTH_QR,
QRMatrix::pivoting::TRUE
);
verify_pivoting(QRM, A);
}
}
// Verify the Moore-Penrose pseudo-inverse function
template
void verify_pinv
(
const MatrixType& A
)
{
const scalar absTol = 1e-6;
Info<< "## Verify Moore-Penrose pseudo-inverse: pinv()" << nl << endl;
const MatrixType APlus(MatrixTools::pinv(A));
#if PRINTALL
InfoNumPyFormat(A, "A");
InfoNumPyFormat(APlus, "A^+");
#endif
{
const MatrixType APlusPlus(MatrixTools::pinv(APlus));
cmp("# (A^+)^+ = A: ", flt(APlusPlus), flt(A), absTol);
}
// The Moore-Penrose conditions
// w.wiki/5RL (Retrieved: 29-06-19)
{
const MatrixType AAPlusA((A*APlus)*A);
cmp("# A*(A^+)*A = A: ", flt(AAPlusA), flt(A), absTol);
}
{
const MatrixType APlusAAPlus((APlus*A)*APlus);
cmp("# (A^+)*A*(A^+) = A: ", flt(APlusAAPlus), flt(APlus), absTol);
}
{
const MatrixType AAPlus(A*APlus);
const MatrixType AAPlusT(AAPlus.T());
cmp("# (A*(A^+)).T() = A*(A^+): ", flt(AAPlusT), flt(AAPlus), absTol);
}
{
const MatrixType APlusA(APlus*A);
const MatrixType APlusAT(APlusA.T());
cmp("# ((A^+)*A = ((A^+)*A).T(): ", flt(APlusA), flt(APlusAT), absTol);
}
}
// Verify the direct solution of a linear system by QRMatrix
void verify_solve
(
const SquareMatrix& A
)
{
// do nothing
}
// Verify the direct solution of a linear system by QRMatrix
void verify_solve
(
const SquareMatrix& A
)
{
Info<< "## Verify direct solution of A*x = b with A = Q*R:" << nl << endl;
typedef SquareMatrix SMatrix;
const scalar absTol = 1e-6;
// Direct solution of a linear system by QR decomposition
QRMatrix QRM
(
A,
QRMatrix::modes::FULL,
QRMatrix::outputs::BOTH_QR
);
const scalarField residual(A.m(), 0);
const scalarField source(A.m(), 1);
const scalarField x(QRM.solve(source));
const scalarField AxMinusSource(A*x - source);
const scalarField xMinusQRinvSource(x - QRM.inv()*source);
const SMatrix QRinvA(QRM.inv()*A);
const SMatrix identity(A.m(), I);
cmp("# A*x - b = residual: ", AxMinusSource, residual, absTol);
cmp("# x - inv(QR)*b = residual: ", xMinusQRinvSource, residual, absTol);
cmp("# inv(QR)*A = I: ", flt(QRinvA), flt(identity), absTol);
}
// Verify the parallel tall-skinny QR decomposition
template
void verify_tsqr
(
Random& rndGen
)
{
Info<< "## Verify parallel direct tall-skinny QR decomposition:"
<< nl << endl;
typedef RectangularMatrix RMatrix;
// Size of the full matrix and its partitions
const label nColsSum = rndGen.position