diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 7a6b33cd..d97041ef 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -30,6 +30,7 @@ Author: Michael Marshall /* END LEGAL */ #include #include +#include using namespace Grid; using namespace Grid::QCD; @@ -109,126 +110,148 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam std::cout << " done." << std::endl; } -typedef ComplexD TestScalar; -typedef Eigen::TensorFixedSize> TensorRank5UShort; -typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TensorRank5UShortAlt; -typedef Eigen::Tensor TensorRank3; -typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> Tensor_9_4_2; -typedef std::vector aTensor_9_4_2; -typedef Eigen::TensorFixedSize> LSCTensor; +// Perform I/O tests on a range of tensor types +// Test coverage: scalars, complex and GridVectors in single, double and default precision +class TensorIO : public Serializable { + using TestScalar = ComplexD; + using SR3 = Eigen::Sizes<9,4,2>; + using SR5 = Eigen::Sizes<5,4,3,2,1>; + using ESO = Eigen::StorageOptions; + using TensorRank3 = Eigen::Tensor; + using TensorR5 = Eigen::TensorFixedSize; + using TensorR5Alt = Eigen::TensorFixedSize; + using Tensor942 = Eigen::TensorFixedSize; + using aTensor942 = std::vector; + using Perambulator = Eigen::Tensor; + using LSCTensor = Eigen::TensorFixedSize>; + + static const Real FlagR; + static const Complex Flag; + static const ComplexF FlagF; + static const TestScalar FlagTS; + static const char * const pszFilePrefix; -class PerambIOTestClass: Serializable { - ComplexD Flag; + void Init(unsigned short Precision) + { + SequentialInit(Perambulator1, Flag, Precision); + SequentialInit(Perambulator2, Flag, Precision); + SequentialInit(tensorR5, FlagR, Precision); + SequentialInit(tensorRank3, FlagF, Precision); + SequentialInit(tensor_9_4_2, FlagTS, Precision); + for( auto &t : atensor_9_4_2 ) + SequentialInit(t, FlagTS, Precision); + SequentialInit(MyLSCTensor, Flag, Precision); + } + + // Perform an I/O test for a single Eigen tensor (of any type) + template + static void TestOne(const char * MyTypeName, unsigned short Precision, std::string &filename, + const char * pszExtension, unsigned int &TestNum, + typename EigenIO::Traits::scalar_type Flag, IndexTypes... otherDims) + { + using Traits = EigenIO::Traits; + using scalar_type = typename Traits::scalar_type; + std::unique_ptr pTensor{new T(otherDims...)}; + SequentialInit( * pTensor, Flag, Precision ); + filename = pszFilePrefix + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension; + ioTest(filename, * pTensor, MyTypeName, MyTypeName); + } + public: - using PerambTensor = Eigen::Tensor; - GRID_SERIALIZABLE_CLASS_MEMBERS(PerambIOTestClass - , SpinColourVector, spinColourVector - , SpinColourMatrix, spinColourMatrix + GRID_SERIALIZABLE_CLASS_MEMBERS(TensorIO + , SpinColourVector, spinColourVector + , SpinColourMatrix, spinColourMatrix , std::vector, DistilParameterNames , std::vector, DistilParameterValues - , PerambTensor, Perambulator - , PerambTensor, Perambulator2 - , TensorRank5UShort, tensorRank5UShort + , Perambulator, Perambulator1 + , Perambulator, Perambulator2 + , TensorR5, tensorR5 , TensorRank3, tensorRank3 - , Tensor_9_4_2, tensor_9_4_2 - , aTensor_9_4_2, atensor_9_4_2 + , Tensor942, tensor_9_4_2 + , aTensor942, atensor_9_4_2 , LSCTensor, MyLSCTensor ); - PerambIOTestClass() + TensorIO() : DistilParameterNames {"do", "androids", "dream", "of", "electric", "sheep?"} , DistilParameterValues{2,3,1,4,5,1} - , Perambulator(2,3,1,4,5,1) + , Perambulator1(2,3,1,4,5,1) , Perambulator2(7,1,6,1,5,1) , tensorRank3(7,3,2) - , atensor_9_4_2(3) - //, Flag(1,-3.1415927) - , Flag(1,-1) + , atensor_9_4_2(3) {} + +#define TEST_PARAMS( T ) #T, Precision, filename, pszExtension, TestNum + + // Perform a series of I/O tests for Eigen tensors, including a serialisable object + template + static void Test(const char * pszExtension, unsigned short Precision = 0) { - SequentialInit(Perambulator, Flag); - SequentialInit(Perambulator2, Flag); - SequentialInit(tensorRank5UShort); - SequentialInit(tensorRank3, Flag); - SequentialInit(tensor_9_4_2, Flag); - for( auto &t : atensor_9_4_2 ) SequentialInit(t, Flag); - SequentialInit( MyLSCTensor, Flag ); + // Perform a series of tests on progressively more complex tensors + unsigned int TestNum = 0; + std::string filename; + // Rank 1 tensor containing a single integer + using TensorSingle = Eigen::TensorFixedSize>; + TestOne( TEST_PARAMS( TensorSingle ), 7 ); // lucky! + // Rather convoluted way of defining a single complex number + using TensorSimple = Eigen::Tensor, 6>; + using I = typename TensorSimple::Index; // NB: Never specified, so same for all my test tensors + // Try progressively more complicated tensors + TestOne( TEST_PARAMS( TensorSimple ), FlagTS, 1,1,1,1,1,1 ); + TestOne( TEST_PARAMS( TensorRank3 ), FlagF, 6, 3, 2 ); + TestOne(TEST_PARAMS( Tensor942 ), FlagTS); + TestOne(TEST_PARAMS( LSCTensor ), Flag ); + + // Now see whether we can write a tensor in one memory order and read back in the other + { + TestOne(TEST_PARAMS( TensorR5 ), FlagR); + std::cout << " Testing alternate memory order read ... "; + TensorR5Alt t2; + RDR_ reader(filename); + ::Grid::read(reader, "TensorR5", t2); + bool good = true; + TensorR5 cf; + SequentialInit( cf, FlagR, Precision ); + for_all( t2, [&](typename EigenIO::Traits::scalar_type c, I n, + const std::array &TensorIndex, + const std::array::Rank> &GridTensorIndex ){ + Real &r = cf(TensorIndex); + if( c != r ){ + good = false; + std::cout << "\nError: " << n << ": " << c << " != " << r; + } + } ); + if (!good) { + std::cout << std::endl; + dump_tensor(t2,"t2"); + exit(EXIT_FAILURE); + } + std::cout << " done." << std::endl; + } + // Now test a serialisable object containing a number of tensors + { + static const char MyTypeName[] = "TensorIO"; + filename = pszFilePrefix + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension; + std::unique_ptr pObj{new TensorIO()}; + pObj->Init(Precision); + ioTest(filename, * pObj, MyTypeName, MyTypeName, Precision); + } + // Stress test. Too large for the XML or text readers and writers! +#ifdef STRESS_TEST + const std::type_info &tw = typeid( WTR_ ); + if( tw == typeid( Hdf5Writer ) || tw == typeid( BinaryWriter ) ) { + using LCMTensor=Eigen::TensorFixedSize,2>,7>,3>, + Eigen::Sizes<2,4,11,10,9>, Eigen::StorageOptions::RowMajor>; + std::cout << "sizeof( LCMTensor ) = " << sizeof( LCMTensor ) / 1024 / 1024 << " MB" << std::endl; + TestOne(TEST_PARAMS( LCMTensor ), Flag); + } +#endif } }; -#define TEST_PARAMS( T ) #T, Flag, Precision, filename, pszExtension, TestNum - -// Perform an I/O test for a single Eigen tensor (of any type) -template -void EigenTensorTestSingle(const char * MyTypeName, typename EigenIO::Traits::scalar_type Flag, - unsigned short Precision, std::string &filename, const char * pszExtension, - unsigned int &TestNum, IndexTypes... otherDims) -{ - using Traits = EigenIO::Traits; - using scalar_type = typename Traits::scalar_type; - std::unique_ptr pTensor{new T(otherDims...)}; - SequentialInit( * pTensor, Flag, Precision ); - filename = "iotest_" + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension; - ioTest(filename, * pTensor, MyTypeName, MyTypeName); -} - -// Perform a series of I/O tests for Eigen tensors, including a serialisable object -template -void EigenTensorTest(const char * pszExtension, unsigned short Precision = 0) -{ - // Perform a series of tests on progressively more complex tensors - unsigned int TestNum = 0; - std::string filename; - { - int Flag = 7; - using TensorSingle = Eigen::TensorFixedSize>; - EigenTensorTestSingle(TEST_PARAMS( TensorSingle )); - } - TestScalar Flag{1,-3.1415927}; - using TensorSimple = Eigen::Tensor, 6>; - using I = typename TensorSimple::Index; - EigenTensorTestSingle( TEST_PARAMS( TensorSimple ), 1, 1, 1, 1, 1, 1 ); - EigenTensorTestSingle( TEST_PARAMS( TensorRank3 ), 6, 3, 2 ); - EigenTensorTestSingle(TEST_PARAMS( Tensor_9_4_2 )); - EigenTensorTestSingle(TEST_PARAMS( LSCTensor )); - // Now see whether we could write out a tensor in one memory order and read back in the other - { - unsigned short Flag = 1; - EigenTensorTestSingle(TEST_PARAMS( TensorRank5UShort )); - std::cout << " Testing alternate memory order read ... "; - TensorRank5UShortAlt t2; - RDR_ reader(filename); - read(reader, "TensorRank5UShort", t2); - bool good = true; - using Index = typename TensorRank5UShortAlt::Index; - // NB: I can't call - for_all( t2, [&](unsigned short c, Index n, - const std::array &TensorIndex, - const std::array::Rank> &GridTensorIndex ){ - good = good && ( c == n ); - } ); - if (!good) { - std::cout << " failure!" << std::endl; - dump_tensor(t2,"t2"); - exit(EXIT_FAILURE); - } - std::cout << " done." << std::endl; - } - // Now test a serialisable object containing a number of tensors - { - static const char MyTypeName[] = "PerambIOTestClass"; - std::unique_ptr pObj{new PerambIOTestClass()}; - filename = "iotest_" + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension; - ioTest(filename, * pObj, MyTypeName, MyTypeName); - } - // Stress test. Too large for the XML or text readers and writers! -#ifdef STRESS_TEST - if( typeid( WTR_ ).name() == typeid( Hdf5Writer ).name() || typeid( WTR_ ).name() == typeid( BinaryWriter ).name() ) { - using LCMTensor=Eigen::TensorFixedSize,2>,7>,3>, - Eigen::Sizes<2,4,11,10,9>, Eigen::StorageOptions::RowMajor>; - std::cout << "sizeof( LCMTensor ) = " << sizeof( LCMTensor ) / 1024 / 1024 << " MB" << std::endl; - EigenTensorTestSingle(TEST_PARAMS( LCMTensor )); - } -#endif -} +const Real TensorIO::FlagR {-1.001}; +const Complex TensorIO::Flag {1,-3.1415927}; +const ComplexF TensorIO::FlagF {1,-3.1415927}; +const TensorIO::TestScalar TensorIO::FlagTS{1,-3.1415927}; +const char * const TensorIO::pszFilePrefix = "tensor_"; template void tensorConvTestFn(GridSerialRNG &rng, const std::string label) @@ -314,14 +337,14 @@ int main(int argc,char **argv) ioTest("iotest.h5", obj, "HDF5 (object) "); ioTest("iotest.h5", vec, "HDF5 (vector of objects)"); std::cout << "\n==== detailed Hdf5 tensor tests (Grid::EigenIO)" << std::endl; - EigenTensorTest(".h5"); + TensorIO::Test(".h5"); #endif std::cout << "\n==== detailed binary tensor tests (Grid::EigenIO)" << std::endl; - EigenTensorTest(".bin"); + TensorIO::Test(".bin"); std::cout << "\n==== detailed xml tensor tests (Grid::EigenIO)" << std::endl; - EigenTensorTest(".xml", 6); + TensorIO::Test(".xml", 6); std::cout << "\n==== detailed text tensor tests (Grid::EigenIO)" << std::endl; - EigenTensorTest(".dat", 5); + TensorIO::Test(".dat", 5); std::cout << "\n==== vector flattening/reconstruction" << std::endl; typedef std::vector>> vec3d;