/*---------------------------------------------------------------------------*\ ========= | \\ / 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