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