/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2020 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-RectangularMatrix Description Tests for \c RectangularMatrix constructors, member functions, member operators, global functions, global operators, and friend functions using \c floatScalar, \c doubleScalar, and \c complex base types. Cross-checks were obtained from 'NumPy 1.15.1' if no theoretical cross-check exists (like eigendecomposition relations), and were hard-coded for elementwise comparisons. For \c complex base type, the cross-checks do only involve zero imag part. Note Pending tests were tagged as "## Pending ##". \*---------------------------------------------------------------------------*/ #include "RectangularMatrix.H" #include "SquareMatrix.H" #include "scalar.H" #include "complex.H" #include "IOmanip.H" #include "TestTools.H" using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // Create each constructor of RectangularMatrix, and print output template void test_constructors(Type) { { Info<< "# Construct a square matrix (rows == columns):" << nl; const RectangularMatrix A(5); Info<< A << endl; } { Info<< "# Construct given number of rows/columns:" << nl; const RectangularMatrix A(2, 4); Info<< A << endl; } { Info<< "# Construct given number of rows/columns " << "initializing all elements to zero:" << nl; const RectangularMatrix A(2, 4, Zero); Info<< A << endl; } { Info<< "# Construct given number of rows/columns " << "initializing all elements to a value:" << nl; const RectangularMatrix A(2, 4, Type(3)); Info<< A << endl; } { Info<< "# Construct given number of rows/columns " << "initializing all elements to zero, and diagonal to one:" << nl; const RectangularMatrix A(labelPair(2, 4), I); Info<< A << endl; } { Info<< "# Construct given number of rows/columns " << "by using a label pair:" << nl; const RectangularMatrix A(labelPair(2, 4)); Info<< A << endl; } { Info<< "# Construct given number of rows/columns by using a label pair " << "and initializing all elements to zero:" << nl; const RectangularMatrix A(labelPair(2, 4), Zero); Info<< A << endl; } { Info<< "# Construct given number of rows/columns by using a label pair " << "and initializing all elements to the given value:" << nl; const RectangularMatrix A(labelPair(2, 4), Type(3)); Info<< A << endl; } { Info<< "# Construct from a block of another matrix:" << nl; const RectangularMatrix B(labelPair(2, 4), Type(3)); const RectangularMatrix A(B.subMatrix(1, 1)); Info<< A << endl; } { Info<< "# Construct from a block of another matrix:" << nl; RectangularMatrix B(labelPair(2, 4), Type(3)); const RectangularMatrix A(B.subMatrix(1, 1)); Info<< A << endl; } { Info<< "#: Construct as copy of a square matrix:" << nl; const SquareMatrix B(5); const RectangularMatrix A(B); Info<< A << endl; } } // Execute each member function of RectangularMatrix, and print output template void test_member_funcs(Type) { RectangularMatrix A(labelPair(2, 3), Zero); assignMatrix ( A, { Type(1), Type(-2.2), Type(-3.4), Type(-0.35), Type(1), Type(5) } ); Info<< "# Operand: " << nl << " RectangularMatrix = " << A << endl; // Matrix.H { Info<< "# Access:" << nl; cmp(" The number of rows = ", Type(A.m()), Type(2)); cmp(" The number of columns = ", Type(A.n()), Type(3)); cmp(" The number of elements in Matrix = ", Type(A.size()), Type(6)); cmp(" Return row/column sizes = ", A.sizes(), labelPair(2, 3)); cmp(" Return true if Matrix is empty = ", A.empty(), false); cmp ( " Return const pointer to the first data elem = ", *(A.cdata()), Type(1) ); cmp ( " Return pointer to the first data elem = ", *(A.data()), Type(1) ); cmp ( " Return const pointer to data in the specified row = ", *(A.rowData(1)), Type(-0.35) ); cmp ( " Return pointer to data in the specified row = ", *(A.rowData(1)), Type(-0.35) ); const Type& a = A.at(4); cmp(" Linear addressing const element access = ", a, Type(1)); Type& b = A.at(4); cmp(" Linear addressing element access = ", b, Type(1)); } { Info<< "# Block access (const):" << nl; const RectangularMatrix Acol(A.subColumn(1)); cmp ( " Return const column or column's subset of Matrix = ", flt(Acol), List({Type(-2.2), Type(1)}) ); const RectangularMatrix Arow(A.subRow(1)); cmp ( " Return const row or row's subset of Matrix = ", flt(Arow), List({Type(-0.35), Type(1), Type(5)}) ); const RectangularMatrix Amat(A.subMatrix(1, 1)); cmp ( " Return const sub-block of Matrix = ", flt(Amat), List({Type(1), Type(5)}) ); } { Info<< "# Block access (non-const):" << nl; RectangularMatrix Acol(A.subColumn(1)); cmp ( " Return column or column's subset of Matrix = ", flt(Acol), List({Type(-2.2), Type(1)}) ); RectangularMatrix Arow(A.subRow(1)); cmp ( " Return row or row's subset of Matrix = ", flt(Arow), List({Type(-0.35), Type(1), Type(5)}) ); RectangularMatrix Amat(A.subMatrix(1, 1)); cmp ( " Return sub-block of Matrix = ", flt(Amat), List({Type(1), Type(5)}) ); } { Info<< "# Check:" << nl; A.checki(0); A.checkj(1); A.checkSize(); cmp(" Check all entries have identical values = ", A.uniform(), false); } { Info<< "# Edit:" << nl; RectangularMatrix cpA(A); cpA.clear(); cmp(" Clear Matrix, i.e. set sizes to zero = ", cpA.empty(), true); RectangularMatrix cpA1(A); cmp ( " Release storage management of Matrix contents by transferring " "management to a List = ", (cpA1.release()), List ({ Type(1), Type(-2.2), Type(-3.4), Type(-0.35), Type(1), Type(5) }) ); RectangularMatrix cpA2(A); cpA2.swap(cpA1); cmp(" Swap contents = ", flt(cpA1), flt(A)); cpA2.transfer(cpA1); cmp ( " Transfer the contents of the argument Matrix into this Matrix " "and annul the argument MatrixSwap contents = ", flt(cpA2), flt(A) ); cpA2.resize(1, 2); cmp ( " Change Matrix dimensions, preserving the elements = ", flt(cpA2), List({Type(1), Type(-2.2)}) ); RectangularMatrix cpA3(A); cpA3.setSize(1, 2); cmp ( " Change Matrix dimensions, preserving the elements = ", flt(cpA3), List({Type(1), Type(-2.2)}) ); RectangularMatrix cpA4(A); cpA4.shallowResize(1, 2); cmp ( " Resize Matrix without reallocating storage (unsafe) = ", flt(cpA4), List({Type(1), Type(-2.2)}) ); RectangularMatrix smallA(labelPair(2, 3), Type(VSMALL)); smallA.round(); cmp ( " Round elements with mag smaller than tol (SMALL) to zero = ", flt(smallA), List(6, Zero) ); } { Info<< "# Operations:" << nl; cmp(" Transpose = ", flt((A.T()).T()), flt(A)); RectangularMatrix cA(labelPair(2, 2), Zero); assignMatrix ( cA, { complex(2, -1.2), complex(4.1, 0.4), complex(8.1, -1.25), complex(7.3, -1.4) } ); RectangularMatrix cAT(labelPair(2, 2), Zero); assignMatrix ( cAT, { complex(2, 1.2), complex(8.1, 1.25), complex(4.1, -0.4), complex(7.3, 1.4) } ); cmp(" Hermitian transpose = ", flt(cA.T()), flt(cAT)); RectangularMatrix B(3, 3, Zero); assignMatrix ( B, { Type(4.1), Type(12.5), Type(-16.3), Type(-192.3), Type(-9.1), Type(-3.0), Type(1.0), Type(5.02), Type(-4.4) } ); const RectangularMatrix cpB(B); const List lst({Type(1), Type(2), Type(3)}); const Field out1(B*lst); const Field out2(B.Amul(lst)); const Field out3(lst*B); const Field out4(B.Tmul(lst)); const Field rsult1({Type(-19.8), Type(-219.5), Type(-2.16)}); const Field rsult2({Type(-377.5), Type(9.36), Type(-35.5)}); cmp ( " Right-multiply Matrix by a List (A * x) = ", out1, rsult1, 1e-4 ); cmp ( " Right-multiply Matrix by a column vector A.Amul(x) = ", out1, out2, 1e-4 ); cmp ( " Left-multiply Matrix by a List (x * A) = ", out3, rsult2, 1e-4 ); cmp ( " Left-multiply Matrix by a row vector A.Tmul(x) = ", out4, rsult2, 1e-4 ); List diagB1({Type(4.1), Type(-9.1), Type(-4.4)}); cmp ( " Extract the diagonal elements = ", B.diag(), diagB1 ); List diagB2({Type(-100), Type(-100), Type(-100)}); B.diag(diagB2); cmp ( " Assign diagonal of Matrix = ", B.diag(), diagB2 ); B = cpB; cmp(" Trace = ", B.trace(), Type(-9.4), 1e-4); cmp ( " Return L2-Norm of chosen column = ", B.columnNorm(0), 192.34630227794867, 1e-4 ); cmp ( " Return L2-Norm of chosen column (noSqrt=true) = ", B.columnNorm(0, true), 36997.1, 1e-2 ); cmp ( " Return Frobenius norm of Matrix = ", B.norm(), 193.7921835369012, 1e-4 ); } } // Execute each member operators of RectangularMatrix, and print output template void test_member_opers(Type) { RectangularMatrix A(labelPair(2, 4), I); const RectangularMatrix cpA(A); Info<< "# Operand: " << nl << " RectangularMatrix = " << A << endl; A = Zero; cmp(" Assign all elements to zero =", flt(A), List(8, Zero)); A = cpA; A = Type(5); cmp(" Assign all elements to value =", flt(A), List(8, Type(5))); A = cpA; const Type* a = A[1]; cmp ( " Return const pointer to data in the specified row = ", *a, Type(0) ); Type* b = A[1]; cmp ( " Return pointer to data in the specified row = ", *b, Type(0) ); const Type& c = A(1, 1); cmp ( " (i, j) const element access operator = ", c, Type(1) ); Type& d = A(1, 1); cmp ( " (i, j) element access operator = ", d, Type(1) ); // ## Pending ## // Copy assignment // Move assignment // Assignment to a block of another Matrix // Assignment to a block of another Matrix // ############# A = Zero; cmp ( " Assignment of all elements to zero = ", flt(A), flt(RectangularMatrix(2, 4, Zero)) ); A = cpA; A = Type(-1.2); cmp ( " Assignment of all elements to the given value = ", flt(A), flt(RectangularMatrix(2, 4, Type(-1.2))) ); A = cpA; A += cpA; cmp ( " Matrix addition =", flt(A), flt(Type(2)*cpA) ); A = cpA; A -= cpA; cmp ( " Matrix subtraction = ", flt(A), flt(RectangularMatrix(2, 4, Zero)) ); A = cpA; A = Zero; A += Type(5); cmp ( " Matrix scalar addition = ", flt(A), flt(RectangularMatrix(2, 4, Type(5))) ); A = cpA; A = Zero; A -= Type(5); cmp ( " Matrix scalar subtraction = ", flt(A), flt(RectangularMatrix(2, 4, Type(-5))) ); A = cpA; A = Type(1); A *= Type(5); cmp ( " Matrix scalar multiplication = ", flt(A), flt(RectangularMatrix(2, 4, Type(5))) ); A = cpA; A = Type(1); A /= Type(5); cmp ( " Matrix scalar division = ", flt(A), flt(RectangularMatrix(2, 4, Type(0.2))) ); A = cpA; A(0,0) = Type(-10.5); Type* i0 = A.begin(); cmp ( " Return an iterator to begin traversing a Matrix = ", *i0, Type(-10.5) ); A(1,3) = Type(20); Type* i1 = A.end(); cmp ( " Return an iterator to end traversing a Matrix = ", *(--i1), Type(20) ); const Type* i2 = A.cbegin(); cmp ( " Return const iterator to begin traversing a Matrix = ", *i2, Type(-10.5) ); const Type* i3 = A.cend(); cmp ( " Return const iterator to end traversing a Matrix = ", *(--i3), Type(20) ); const Type* i4 = A.begin(); cmp ( " Return const iterator to begin traversing a Matrix = ", *i4, Type(-10.5) ); const Type* i5 = A.end(); cmp ( " Return const iterator to end traversing a Matrix = ", *(--i5), Type(20) ); // ## Pending ## // Read Matrix from Istream, discarding existing contents // Write Matrix, with line-breaks in ASCII when length exceeds shortLen // ############# } // Execute each friend function of RectangularMatrix, and print output template void test_friend_funcs(Type) { const Field F ({ Type(1), Type(-2.2), Type(10), Type(-0.1) }); Info<< "# Operand: " << nl << " Field = " << F << endl; { RectangularMatrix A(labelPair(4, 4), Zero); assignMatrix ( A, { Type(1), Type(-2.2), Type(10), Type(-0.1), Type(-2.2), Type(4.84), Type(-22), Type(0.22), Type(10), Type(-22), Type(100), Type(-1), Type(-0.1), Type(0.22), Type(-1), Type(0.01) } ); cmp ( " Return the outer product of Field-Field as RectangularMatrix = ", flt(outer(F, F)), flt(A), 1e-6 ); } } // Execute each global function of RectangularMatrix, and print output template void test_global_funcs(Type) { RectangularMatrix A(labelPair(2, 3), Zero); assignMatrix ( A, { Type(1), Type(-2.2), Type(-3.4), Type(-0.35), Type(10.32), Type(5) } ); const RectangularMatrix cpA(A); Info<< "# Operand: " << nl << " RectangularMatrix = " << A << endl; // ## Pending ## // cmp(" Find max value in Matrix = ", max(A), Type(10.32)); // cmp(" Find min value in Matrix = ", min(A), Type(-3.4)); // cmp // ( // " Find min-max value in Matrix = ", // minMax(A), // MinMax(10.32, -3.4) // ); // ############# } // Execute each global operators of RectangularMatrix, and print output template void test_global_opers(Type) { RectangularMatrix A(labelPair(2, 3), Zero); assignMatrix ( A, { Type(1), Type(-2.2), Type(-3.4), Type(-0.35), Type(10.32), Type(5) } ); const RectangularMatrix cpA(A); Info<< "# Operand: " << nl << " RectangularMatrix = " << A << endl; // Matrix.C cmp(" Matrix negation = ", flt(-A), flt(Type(-1)*A)); cmp(" Matrix addition = ", flt(A + A), flt(Type(2)*A)); cmp(" Matrix subtraction = ", flt(A - A), flt(Type(0)*A)); cmp(" Scalar multiplication = ", flt(Type(2)*A), flt(A + A)); cmp(" Scalar multiplication = ", flt(A*Type(2)), flt(A + A)); cmp ( " Scalar addition = ", flt(Type(0)*A + Type(1)), flt(RectangularMatrix(A.sizes(), Type(1))) ); cmp ( " Scalar addition = ", flt(Type(1) + Type(0)*A), flt(RectangularMatrix(A.sizes(), Type(1))) ); cmp ( " Scalar subtraction = ", flt(Type(0)*A - Type(1)), flt(RectangularMatrix(A.sizes(), Type(-1))) ); cmp ( " Scalar subtraction = ", flt(Type(1) - Type(0)*A), flt(RectangularMatrix(A.sizes(), Type(1))) ); cmp ( " Scalar division = ", flt((Type(1) + Type(0)*A)/Type(2.5)), flt(RectangularMatrix(A.sizes(), Type(0.4))) ); RectangularMatrix innerA(2, 2, Zero); assignMatrix ( innerA, { Type(17.4), Type(-40.054), Type(-40.054), Type(131.6249) } ); const RectangularMatrix A1(A); const RectangularMatrix A2(A.T()); cmp ( " Matrix-Matrix multiplication using ikj-algorithm = ", flt(A1*A2), flt(innerA), 1e-1 ); cmp ( " Implicit A.T()*B = ", flt(A2&A2), flt(innerA), 1e-1 ); RectangularMatrix outerA(3, 3, Zero); assignMatrix ( outerA, { Type(1.1225), Type(-5.812), Type(-5.15), Type(-5.812), Type(111.3424), Type(59.08), Type(-5.15), Type(59.08), Type(36.56) } ); cmp ( " Implicit A*B.T() = ", flt(A2^A2), flt(outerA), 1e-1 ); // MatrixI.H const List lst({Type(1), Type(2), Type(-3)}); const List out1(A*lst); const List out2(lst*A.T()); const List rslt1({Type(6.8), Type(5.29)}); const List rslt2({Type(6.8), Type(5.29)}); cmp ( " Matrix-vector multiplication (A * x), where x is a column vector = ", out1, rslt1, 1e-1 ); cmp ( " Vector-matrix multiplication (x * A), where x is a row vector = ", out2, rslt2, 1e-1 ); } // Do compile-time recursion over the given types template inline typename std::enable_if::type run_tests(const std::tuple& types, const List& typeID){} template inline typename std::enable_if::type run_tests(const std::tuple& types, const List& typeID) { Info<< nl << " ## Test constructors: "<< typeID[I] <<" ##" << nl; test_constructors(std::get(types)); Info<< nl << " ## Test member functions: "<< typeID[I] <<" ##" << nl; test_member_funcs(std::get(types)); Info<< nl << " ## Test member opers: "<< typeID[I] <<" ##" << nl; test_member_opers(std::get(types)); Info<< nl << " ## Test global functions: "<< typeID[I] << " ##" << nl; test_global_funcs(std::get(types)); Info<< nl << " ## Test global operators: "<< typeID[I] << " ##" << nl; test_global_opers(std::get(types)); Info<< nl << " ## Test friend funcs: "<< typeID[I] <<" ##" << nl; test_friend_funcs(std::get(types)); run_tests(types, typeID); } // * * * * * * * * * * * * * * * Main Program * * * * * * * * * * * * * * * // int main() { Info<< setprecision(15); const std::tuple types ( std::make_tuple(Zero, Zero, Zero) ); const List typeID ({ "RectangularMatrix", "RectangularMatrix", "RectangularMatrix" }); run_tests(types, typeID); if (nFail_) { Info<< nl << " #### " << "Failed in " << nFail_ << " tests " << "out of total " << nTest_ << " tests " << "####\n" << endl; return 1; } Info<< nl << " #### Passed all " << nTest_ <<" tests ####\n" << endl; return 0; } // ************************************************************************* //