diff --git a/applications/test/QRMatrix/Make/files b/applications/test/QRMatrix/Make/files new file mode 100644 index 0000000000..9825bbfcf3 --- /dev/null +++ b/applications/test/QRMatrix/Make/files @@ -0,0 +1,3 @@ +Test-QRMatrix.C + +EXE = $(FOAM_USER_APPBIN)/Test-QRMatrix diff --git a/applications/test/QRMatrix/Make/options b/applications/test/QRMatrix/Make/options new file mode 100644 index 0000000000..4e772fdf9d --- /dev/null +++ b/applications/test/QRMatrix/Make/options @@ -0,0 +1,2 @@ +/* EXE_INC = -I$(LIB_SRC)/finiteVolume/lnInclude */ +/* EXE_LIBS = -lfiniteVolume */ diff --git a/applications/test/QRMatrix/Test-QRMatrix.C b/applications/test/QRMatrix/Test-QRMatrix.C new file mode 100644 index 0000000000..fb22d78bde --- /dev/null +++ b/applications/test/QRMatrix/Test-QRMatrix.C @@ -0,0 +1,1484 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2019 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 . + +\*---------------------------------------------------------------------------*/ + +#include "MatrixTools.H" +#include "QRMatrix.H" +#include "Random.H" +#include "IOmanip.H" + +using namespace Foam; +using namespace Foam::MatrixTools; + +#define equal MatrixTools::equal +#define RUNALL true +const bool verbose = true; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +void horizontalLine() +{ + Info<< "+---------+---------+---------+---------+---------+" << nl; +} + + +// Create random scalar-type matrix +template +typename std::enable_if +< + !std::is_same::value, + MatrixType +>::type makeRandomMatrix +( + const labelPair& dims, + Random& rndGen +) +{ + MatrixType mat(dims); + + std::generate + ( + mat.begin(), + mat.end(), + [&]{ return rndGen.GaussNormal(); } + ); + + return mat; +} + + +// Create random complex-type matrix +template +typename std::enable_if +< + std::is_same::value, + MatrixType +>::type makeRandomMatrix +( + const labelPair& dims, + Random& rndGen +) +{ + MatrixType mat(dims); + + std::generate + ( + mat.begin(), + mat.end(), + [&] + { + return complex + ( + rndGen.GaussNormal(), + rndGen.GaussNormal() + ); + } + ); + + return mat; +} + + +// Print OpenFOAM matrix in NumPy format +template +void InfoNumPyFormat +( + const MatrixType& mat, + const word objName +) +{ + Info<< objName << ": " << mat.m() << "x" << mat.n() << nl; + for (label m = 0; m < mat.m(); ++m) + { + Info<< "["; + for (label n = 0; n < mat.n(); ++n) + { + if (n == mat.n() - 1) + { + Info<< mat(m,n); + } + else + { + Info<< mat(m,n) << ","; + } + } + if (m == mat.m() - 1) + { + Info << "]" << nl; + } + else + { + Info << "]," << nl; + } + } +} + + +// Returns true if two scalars are equal within a given tolerance, and v.v. +bool isEqual +( + const scalar a, + const scalar b, + const bool verbose = false, + const scalar relTol = 1e-5, + const scalar absTol = 1e-8 +) +{ + if ((absTol + relTol*mag(b)) < mag(a - b)) + { + if (verbose) + { + Info<< "Elements are not close in terms of tolerances:" + << nl << a << tab << b << nl; + } + return false; + } + + if (verbose) + { + Info<< "All elems are the same within the tolerances" << nl; + } + + return true; +} + + +// Prints true if a given matrix is empty, and v.v. +template +void isEmpty +( + const MatrixType& A, + const word objName +) +{ + Info<< "Empty " << objName << " = "; + if (A.empty()) + { + Info<< "true" << nl; + } + else + { + Info<< "false" << nl; + } +} + + +// Checks if Q matrix is a unitary matrix +template +void cross_check_QRMatrix +( + const MatrixType& Aorig, + const MatrixType& Q +) +{ + InfoNumPyFormat(Q, "Q"); + + // mathworld.wolfram.com/UnitaryMatrix.html (Retrieved:18-06-19) + Info<< nl << "# Q.T()*Q = Q*Q.T() = I:" << nl; + const MatrixType QTQ(Q&Q); + const MatrixType QQT(Q^Q); + MatrixType IMatrix({Q.m(), Q.n()}, Zero); + for (label i = 0; i < IMatrix.n(); ++i) + { + IMatrix(i,i) = pTraits::one; + } + equal(QTQ, IMatrix, verbose); + equal(QQT, IMatrix, verbose); + equal(QTQ, QQT, verbose); +} + + +// Checks if R matrix is an upper-triangular matrix +template +void cross_check_QRMatrix +( + const MatrixType& R +) +{ + InfoNumPyFormat(R, "R"); + + // mathworld.wolfram.com/UpperTriangularMatrix.html (Retrieved:18-06-19) + Info<< nl << "# R(i, j) = 0 for i > j:" << nl; + for (label i = 0; i < R.m(); ++i) + { + for (label j = 0; j < R.n(); ++j) + { + if (j < i) + { + isEqual(mag(R(i, j)), 0.0, verbose); + } + } + } +} + + +// Checks if the given matrix can be reconstructed by Q and R matrices +template +void cross_check_QRMatrix +( + const MatrixType& Aorig, + const MatrixType& Q, + const MatrixType& R +) +{ + InfoNumPyFormat(Aorig, "Aorig"); + + cross_check_QRMatrix(Aorig, Q); + + cross_check_QRMatrix(R); + + // mathworld.wolfram.com/QRDecomposition.html (Retrieved:18-06-19) + Info<< nl << "# Q*R = A:" << nl; + const MatrixType AReconstruct(Q*R); + equal(Aorig, AReconstruct, verbose); +} + + +// Checks if R matrix is an upper-triangular matrix, and obeys column pivoting +template +void cross_check_QRMatrix +( + const MatrixType& Aorig, + const labelList& orderP, + const SquareMatrix& P, + const MatrixType& R +) +{ + InfoNumPyFormat(Aorig, "Aorig"); + + InfoNumPyFormat(P, "P"); + + cross_check_QRMatrix(R); + + // w.wiki/54m (Retrieved:20-06-19) + Info<< nl << "# Diag elems of R non-increasing:" + << "|R11| >= |R22| >= ... >= |Rnn|:" << nl; + const List diag0(mag(R.diag())); + List diag1(diag0); + Foam::sort(diag1, std::greater()); + forAll(diag0, i) + { + isEqual(diag0[i], diag1[i], verbose); + } +} + + +// Checks if the given matrix can be reconstructed by column-pivoted Q and R +template +void cross_check_QRMatrix +( + const MatrixType& Aorig, + const MatrixType& Q, + const labelList& orderP, + const SquareMatrix& P, + const MatrixType& R +) +{ + cross_check_QRMatrix(Aorig, orderP, P, R); + cross_check_QRMatrix(Aorig, Q); + + // w.wiki/54m (Retrieved:20-06-19) + Info<< nl << "# Q*R = A*P:" << nl; + const MatrixType AReconstruct(Q*R); + const MatrixType AP(Aorig*P); + equal(AP, AReconstruct, verbose); +} + + +// Checks each constructor of QRMatrix type +template +void verification_QRMatrix +( + MatrixType& A +) +{ + typedef SquareMatrix SMatrix; + + // Create working copies of matrix A + const MatrixType Aorig(A); + + // QRMatrix Constructors + #if (0 | RUNALL) + { + QRMatrix QRNull(); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# FULL_R | OUT_OF_PLACE" << nl; + QRMatrix QRM + ( + QRMatrix::outputTypes::FULL_R, + QRMatrix::storeMethods::OUT_OF_PLACE + ); + QRM.decompose(A); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + isEmpty(Q, "Q"); + cross_check_QRMatrix(R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# FULL_R | IN_PLACE" << nl; + QRMatrix QRM + ( + QRMatrix::outputTypes::FULL_R, + QRMatrix::storeMethods::IN_PLACE + ); + MatrixType A0(A); + QRM.decompose(A0); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + isEmpty(Q, "Q"); + isEmpty(R, "R"); + cross_check_QRMatrix(A0); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# FULL_QR | OUT_OF_PLACE" << nl; + QRMatrix QRM + ( + QRMatrix::outputTypes::FULL_QR, + QRMatrix::storeMethods::OUT_OF_PLACE + ); + QRM.decompose(A); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + cross_check_QRMatrix(Aorig, Q, R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# FULL_QR | IN_PLACE" << nl; + QRMatrix QRM + ( + QRMatrix::outputTypes::FULL_QR, + QRMatrix::storeMethods::IN_PLACE + ); + MatrixType A0(A); + QRM.decompose(A0); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + isEmpty(R, "R"); + cross_check_QRMatrix(Aorig, Q, A0); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# FULL_R | OUT_OF_PLACE | colPivot = on" << nl; + QRMatrix QRM + ( + QRMatrix::outputTypes::FULL_R, + QRMatrix::storeMethods::OUT_OF_PLACE, + QRMatrix::colPivoting::TRUE + ); + QRM.decompose(A); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + isEmpty(Q, "Q"); + cross_check_QRMatrix(Aorig, PList, P, R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# FULL_R | IN_PLACE | colPivot = on" << nl; + QRMatrix QRM + ( + QRMatrix::outputTypes::FULL_R, + QRMatrix::storeMethods::IN_PLACE, + QRMatrix::colPivoting::TRUE + ); + MatrixType A0(A); + QRM.decompose(A0); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + isEmpty(Q, "Q"); + isEmpty(R, "R"); + cross_check_QRMatrix(Aorig, PList, P, A0); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# FULL_QR | OUT_OF_PLACE | colPivot = on" << nl; + QRMatrix QRM + ( + QRMatrix::outputTypes::FULL_QR, + QRMatrix::storeMethods::OUT_OF_PLACE, + QRMatrix::colPivoting::TRUE + ); + QRM.decompose(A); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + cross_check_QRMatrix(Aorig, Q, PList, P, R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# FULL_QR | IN_PLACE | colPivot = on" << nl; + QRMatrix QRM + ( + QRMatrix::outputTypes::FULL_QR, + QRMatrix::storeMethods::IN_PLACE, + QRMatrix::colPivoting::TRUE + ); + MatrixType A0(A); + QRM.decompose(A0); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + isEmpty(R, "R"); + cross_check_QRMatrix(Aorig, Q, PList, P, A0); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# A | FULL_R | OUT_OF_PLACE | colPivot = off" << nl; + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_R, + QRMatrix::storeMethods::OUT_OF_PLACE, + QRMatrix::colPivoting::FALSE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + isEmpty(Q, "Q"); + cross_check_QRMatrix(R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# A | FULL_R | IN_PLACE | colPivot = off" << nl; + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_R, + QRMatrix::storeMethods::IN_PLACE, + QRMatrix::colPivoting::FALSE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + isEmpty(Q, "Q"); + isEmpty(R, "R"); + cross_check_QRMatrix(A0); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# A | FULL_QR | OUT_OF_PLACE | colPivot = off" << nl; + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_QR, + QRMatrix::storeMethods::OUT_OF_PLACE, + QRMatrix::colPivoting::FALSE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + cross_check_QRMatrix(Aorig, Q, R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# A | FULL_QR | IN_PLACE | colPivot = off" << nl; + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_QR, + QRMatrix::storeMethods::IN_PLACE, + QRMatrix::colPivoting::FALSE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + isEmpty(R, "R"); + cross_check_QRMatrix(Aorig, Q, A0); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# A | FULL_R | OUT_OF_PLACE | colPivot = on" << nl; + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_R, + QRMatrix::storeMethods::OUT_OF_PLACE, + QRMatrix::colPivoting::TRUE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + isEmpty(Q, "Q"); + cross_check_QRMatrix(Aorig, PList, P, R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# A | FULL_R | IN_PLACE | colPivot = on" << nl; + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_R, + QRMatrix::storeMethods::IN_PLACE, + QRMatrix::colPivoting::TRUE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + isEmpty(Q, "Q"); + isEmpty(R, "R"); + cross_check_QRMatrix(Aorig, PList, P, A0); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# A | FULL_QR | OUT_OF_PLACE | colPivot = on" << nl; + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_QR, + QRMatrix::storeMethods::OUT_OF_PLACE, + QRMatrix::colPivoting::TRUE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + cross_check_QRMatrix(Aorig, Q, PList, P, R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# A | FULL_QR | IN_PLACE | colPivot = on" << nl; + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_QR, + QRMatrix::storeMethods::IN_PLACE, + QRMatrix::colPivoting::TRUE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + isEmpty(R, "R"); + cross_check_QRMatrix(Aorig, Q, PList, P, A0); + } + #endif + + // constructors with the const type specifier + #if (0 | RUNALL) + { + Info<< "# const A | FULL_R | colPivot = off" << nl; + const MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_R, + QRMatrix::colPivoting::FALSE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + isEmpty(Q, "Q"); + cross_check_QRMatrix(R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# const A | FULL_QR | colPivot = off" << nl; + const MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_QR, + QRMatrix::colPivoting::FALSE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + cross_check_QRMatrix(Aorig, Q, R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# const A | FULL_R | colPivot = on" << nl; + const MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_R, + QRMatrix::colPivoting::TRUE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + isEmpty(Q, "Q"); + cross_check_QRMatrix(Aorig, PList, P, R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# const A | FULL_QR | colPivot = on" << nl; + const MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_QR, + QRMatrix::colPivoting::TRUE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + cross_check_QRMatrix(Aorig, Q, PList, P, R); + } + #endif + + // + #if (0 | RUNALL) + { + Info<< "# REDUCED_R | OUT_OF_PLACE" << nl; + QRMatrix QRM + ( + QRMatrix::outputTypes::REDUCED_R, + QRMatrix::storeMethods::OUT_OF_PLACE + ); + QRM.decompose(A); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + isEmpty(Q, "Q"); + cross_check_QRMatrix(R); + + // check if full R and reduced R give the same answer + if (A.n() < A.m()) + { + QRMatrix fullQRM + ( + QRMatrix::outputTypes::FULL_QR, + QRMatrix::storeMethods::OUT_OF_PLACE + ); + fullQRM.decompose(A); + const MatrixType& fullR(fullQRM.R()); + MatrixType fullR2(fullR.subMatrix(0,0,R.m())); + equal(fullR2, R, verbose); + } + } + #endif + + #if (0 | RUNALL) + { + Info<< "# REDUCED_R | IN_PLACE" << nl; + QRMatrix QRM + ( + QRMatrix::outputTypes::REDUCED_R, + QRMatrix::storeMethods::IN_PLACE + ); + MatrixType A0(A); + QRM.decompose(A0); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + isEmpty(Q, "Q"); + isEmpty(R, "R"); + cross_check_QRMatrix(A0); + + // check if full R and reduced R give the same answer + if (A.n() < A.m()) + { + QRMatrix fullQRM + ( + QRMatrix::outputTypes::FULL_QR, + QRMatrix::storeMethods::IN_PLACE + ); + MatrixType A1(A); + fullQRM.decompose(A1); + MatrixType fullR(A1.subMatrix(0,0,A0.m())); + equal(fullR, A0, verbose); + } + } + #endif + + #if (0 | RUNALL) + { + Info<< "# REDUCED_R | OUT_OF_PLACE | colPivot = on" << nl; + QRMatrix QRM + ( + QRMatrix::outputTypes::REDUCED_R, + QRMatrix::storeMethods::OUT_OF_PLACE, + QRMatrix::colPivoting::TRUE + ); + QRM.decompose(A); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + isEmpty(Q, "Q"); + cross_check_QRMatrix(Aorig, PList, P, R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# REDUCED_R | IN_PLACE | colPivot = on" << nl; + QRMatrix QRM + ( + QRMatrix::outputTypes::REDUCED_R, + QRMatrix::storeMethods::IN_PLACE, + QRMatrix::colPivoting::TRUE + ); + MatrixType A0(A); + QRM.decompose(A0); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + isEmpty(Q, "Q"); + isEmpty(R, "R"); + cross_check_QRMatrix(Aorig, PList, P, A0); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# A | REDUCED_R | OUT_OF_PLACE | colPivot = off" << nl; + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::REDUCED_R, + QRMatrix::storeMethods::OUT_OF_PLACE, + QRMatrix::colPivoting::FALSE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + isEmpty(Q, "Q"); + cross_check_QRMatrix(R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# A | REDUCED_R | IN_PLACE | colPivot = off" << nl; + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::REDUCED_R, + QRMatrix::storeMethods::IN_PLACE, + QRMatrix::colPivoting::FALSE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + isEmpty(Q, "Q"); + isEmpty(R, "R"); + cross_check_QRMatrix(A0); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# A | REDUCED_R | OUT_OF_PLACE | colPivot = on" << nl; + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::REDUCED_R, + QRMatrix::storeMethods::OUT_OF_PLACE, + QRMatrix::colPivoting::TRUE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + isEmpty(Q, "Q"); + cross_check_QRMatrix(Aorig, PList, P, R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# A | REDUCED_R | IN_PLACE | colPivot = on" << nl; + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::REDUCED_R, + QRMatrix::storeMethods::IN_PLACE, + QRMatrix::colPivoting::TRUE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + isEmpty(Q, "Q"); + isEmpty(R, "R"); + cross_check_QRMatrix(Aorig, PList, P, A0); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# const A | REDUCED_R | colPivot = off" << nl; + const MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::REDUCED_R, + QRMatrix::colPivoting::FALSE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + isEmpty(Q, "Q"); + cross_check_QRMatrix(R); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# const A | REDUCED_R | colPivot = on" << nl; + const MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::REDUCED_R, + QRMatrix::colPivoting::TRUE + ); + const MatrixType& Q(QRM.Q()); + const MatrixType& R(QRM.R()); + const labelList& PList(QRM.orderP()); + const SMatrix P(QRM.P()); + isEmpty(Q, "Q"); + cross_check_QRMatrix(Aorig, PList, P, R); + } + #endif +} + + +// Checks the direct solution of a linear system by QRMatrix +template +void verification_QRMatrixSolve +( + MatrixType& A +) +{ + typedef SquareMatrix SMatrix; + + // Create working copies of matrix A + const MatrixType Aorig(A); + + // Direct solution of a linear system by QR decomposition + #if (0 | RUNALL) + { + MatrixType A0(A); + QRMatrix QRM + ( + A0, + QRMatrix::outputTypes::FULL_QR + ); + + Info<< nl << "# Solution of A*x = b with A = Q*R:" << nl; + const scalarField residual(A0.m(), 0); + const scalarField source(A0.m(), 1); + + const scalarField x(QRM.solve(source)); + const scalarField A0xMinusSource(A0*x - source); + const scalarField xMinusQRinvSource(x - QRM.inv()*source); + const SMatrix QRinvA0(QRM.inv()*A0); + const SMatrix identity(A0.m(), I); + + forAll(residual, i) + { + isEqual(A0xMinusSource[i], residual[i], verbose); + isEqual(xMinusQRinvSource[i], residual[i], verbose); + } + equal(QRinvA0, identity, verbose); + } + #endif +} + + +// Checks the parallel tall-skinny QR decomposition +template +void verification_tsqr +( + Random& rndGen +) +{ + typedef RectangularMatrix RMatrix; + + // Size of the full matrix and its partitions + const label nColsSum = rndGen.position(1, 100); + const label qParts = rndGen.position(10, 30); + List