mirror of
https://github.com/paboyle/Grid.git
synced 2025-06-12 20:27:06 +01:00
Eigen::Tensor serialisation. Tested on single and double precision builds
This commit is contained in:
@ -4,11 +4,12 @@
|
||||
|
||||
Source file: ./tests/Test_serialisation.cc
|
||||
|
||||
Copyright (C) 2015-2016
|
||||
Copyright (C) 2015-2019
|
||||
|
||||
Author: Guido Cossu <guido.cossu@ed.ac.uk>
|
||||
Author: Antonin Portelli <antonin.portelli@me.com>
|
||||
Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
Author: Michael Marshall <michael.marshall@ed.ac.uk>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -28,6 +29,8 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
|
||||
*************************************************************************************/
|
||||
/* END LEGAL */
|
||||
#include <Grid/Grid.h>
|
||||
#include <Grid/util/EigenUtil.h>
|
||||
#include <typeinfo>
|
||||
|
||||
using namespace Grid;
|
||||
using namespace Grid::QCD;
|
||||
@ -80,26 +83,176 @@ double d = 2*M_PI;
|
||||
bool b = false;
|
||||
|
||||
template <typename W, typename R, typename O>
|
||||
void ioTest(const std::string &filename, const O &object, const std::string &name)
|
||||
void ioTest(const std::string &filename, const O &object, const std::string &name,
|
||||
const char * tag = "testobject", unsigned short Precision = 0 )
|
||||
{
|
||||
std::cout << "IO test: " << name << " -> " << filename << " ...";
|
||||
// writer needs to be destroyed so that writing physically happens
|
||||
{
|
||||
W writer(filename);
|
||||
|
||||
write(writer, "testobject", object);
|
||||
if( Precision )
|
||||
writer.setPrecision(Precision);
|
||||
write(writer, tag , object);
|
||||
}
|
||||
|
||||
std::cout << " done. reading ...";
|
||||
R reader(filename);
|
||||
O buf;
|
||||
bool good;
|
||||
std::unique_ptr<O> buf( new O ); // In case object too big for stack
|
||||
|
||||
read(reader, "testobject", buf);
|
||||
good = (object == buf);
|
||||
std::cout << name << " IO test: " << (good ? "success" : "failure");
|
||||
std::cout << std::endl;
|
||||
if (!good) exit(EXIT_FAILURE);
|
||||
read(reader, tag, *buf);
|
||||
bool good = Serializable::CompareMember(object, *buf);
|
||||
if (!good) {
|
||||
std::cout << " failure!" << std::endl;
|
||||
if (EigenIO::is_tensor<O>::value)
|
||||
dump_tensor(*buf);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
std::cout << " done." << std::endl;
|
||||
}
|
||||
|
||||
// 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<ComplexF, 3, ESO::RowMajor>;
|
||||
using TensorR5 = Eigen::TensorFixedSize<Real, SR5>;
|
||||
using TensorR5Alt = Eigen::TensorFixedSize<Real, SR5, ESO::RowMajor>;
|
||||
using Tensor942 = Eigen::TensorFixedSize<TestScalar, SR3, ESO::RowMajor>;
|
||||
using aTensor942 = std::vector<Tensor942>;
|
||||
using Perambulator = Eigen::Tensor<SpinColourVector, 6, ESO::RowMajor>;
|
||||
using LSCTensor = Eigen::TensorFixedSize<SpinColourMatrix, Eigen::Sizes<6,5>>;
|
||||
|
||||
static const Real FlagR;
|
||||
static const Complex Flag;
|
||||
static const ComplexF FlagF;
|
||||
static const TestScalar FlagTS;
|
||||
static const char * const pszFilePrefix;
|
||||
|
||||
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 <typename W, typename R, typename T, typename... IndexTypes>
|
||||
static void TestOne(const char * MyTypeName, unsigned short Precision, std::string &filename,
|
||||
const char * pszExtension, unsigned int &TestNum,
|
||||
typename EigenIO::Traits<T>::scalar_type Flag, IndexTypes... otherDims)
|
||||
{
|
||||
using Traits = EigenIO::Traits<T>;
|
||||
using scalar_type = typename Traits::scalar_type;
|
||||
std::unique_ptr<T> pTensor{new T(otherDims...)};
|
||||
SequentialInit( * pTensor, Flag, Precision );
|
||||
filename = pszFilePrefix + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension;
|
||||
ioTest<W, R, T>(filename, * pTensor, MyTypeName, MyTypeName);
|
||||
}
|
||||
|
||||
public:
|
||||
GRID_SERIALIZABLE_CLASS_MEMBERS(TensorIO
|
||||
, SpinColourVector, spinColourVector
|
||||
, SpinColourMatrix, spinColourMatrix
|
||||
, std::vector<std::string>, DistilParameterNames
|
||||
, std::vector<int>, DistilParameterValues
|
||||
, Perambulator, Perambulator1
|
||||
, Perambulator, Perambulator2
|
||||
, TensorR5, tensorR5
|
||||
, TensorRank3, tensorRank3
|
||||
, Tensor942, tensor_9_4_2
|
||||
, aTensor942, atensor_9_4_2
|
||||
, LSCTensor, MyLSCTensor
|
||||
);
|
||||
TensorIO()
|
||||
: DistilParameterNames {"do", "androids", "dream", "of", "electric", "sheep?"}
|
||||
, DistilParameterValues{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) {}
|
||||
|
||||
#define TEST_PARAMS( T ) #T, Precision, filename, pszExtension, TestNum
|
||||
|
||||
// Perform a series of I/O tests for Eigen tensors, including a serialisable object
|
||||
template <typename WTR_, typename RDR_>
|
||||
static void Test(const char * pszExtension, unsigned short Precision = 0)
|
||||
{
|
||||
// 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<Integer, Eigen::Sizes<1>>;
|
||||
TestOne<WTR_, RDR_, TensorSingle>( TEST_PARAMS( TensorSingle ), 7 ); // lucky!
|
||||
// Rather convoluted way of defining a single complex number
|
||||
using TensorSimple = Eigen::Tensor<iMatrix<TestScalar,1>, 6>;
|
||||
using I = typename TensorSimple::Index; // NB: Never specified, so same for all my test tensors
|
||||
// Try progressively more complicated tensors
|
||||
TestOne<WTR_, RDR_, TensorSimple, I,I,I,I,I,I>( TEST_PARAMS( TensorSimple ), FlagTS, 1,1,1,1,1,1 );
|
||||
TestOne<WTR_, RDR_, TensorRank3, I, I, I>( TEST_PARAMS( TensorRank3 ), FlagF, 6, 3, 2 );
|
||||
TestOne<WTR_, RDR_, Tensor942>(TEST_PARAMS( Tensor942 ), FlagTS);
|
||||
TestOne<WTR_, RDR_, LSCTensor>(TEST_PARAMS( LSCTensor ), Flag );
|
||||
|
||||
// Now see whether we can write a tensor in one memory order and read back in the other
|
||||
{
|
||||
TestOne<WTR_, RDR_, TensorR5>(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<TensorR5Alt>::scalar_type c, I n,
|
||||
const std::array<I, TensorR5Alt::NumIndices> &TensorIndex,
|
||||
const std::array<int, EigenIO::Traits<TensorR5Alt>::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<TensorIO> pObj{new TensorIO()};
|
||||
pObj->Init(Precision);
|
||||
ioTest<WTR_, RDR_, TensorIO>(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<iMatrix<iVector<iMatrix<iVector<LorentzColourMatrix,5>,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<WTR_, RDR_, LCMTensor>(TEST_PARAMS( LCMTensor ), Flag);
|
||||
}
|
||||
#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 <typename T>
|
||||
void tensorConvTestFn(GridSerialRNG &rng, const std::string label)
|
||||
{
|
||||
@ -121,12 +274,12 @@ void tensorConvTestFn(GridSerialRNG &rng, const std::string label)
|
||||
int main(int argc,char **argv)
|
||||
{
|
||||
Grid_init(&argc,&argv);
|
||||
|
||||
std::cout << std::boolalpha << "==== basic IO" << std::endl; // display true / false for boolean
|
||||
|
||||
GridSerialRNG rng;
|
||||
|
||||
rng.SeedFixedIntegers(std::vector<int>({42,10,81,9}));
|
||||
|
||||
std::cout << "==== basic IO" << std::endl;
|
||||
|
||||
XmlWriter WR("bother.xml");
|
||||
|
||||
// test basic type writing
|
||||
@ -146,7 +299,6 @@ int main(int argc,char **argv)
|
||||
// test serializable class writing
|
||||
myclass obj(1234); // non-trivial constructor
|
||||
std::vector<myclass> vec;
|
||||
std::pair<myenum, myenum> pair;
|
||||
|
||||
std::cout << "-- serialisable class writing to 'bother.xml'..." << std::endl;
|
||||
write(WR,"obj",obj);
|
||||
@ -154,15 +306,15 @@ int main(int argc,char **argv)
|
||||
vec.push_back(obj);
|
||||
vec.push_back(myclass(5678));
|
||||
vec.push_back(myclass(3838));
|
||||
pair = std::make_pair(myenum::red, myenum::blue);
|
||||
|
||||
write(WR, "objvec", vec);
|
||||
std::cout << "-- serialisable class writing to std::cout:" << std::endl;
|
||||
std::cout << obj << std::endl;
|
||||
std::cout << "-- serialisable class comparison:" << std::endl;
|
||||
std::cout << "vec[0] == obj: " << ((vec[0] == obj) ? "true" : "false") << std::endl;
|
||||
std::cout << "vec[1] == obj: " << ((vec[1] == obj) ? "true" : "false") << std::endl;
|
||||
std::cout << "vec[0] == obj: " << (vec[0] == obj) << std::endl;
|
||||
std::cout << "vec[1] == obj: " << (vec[1] == obj) << std::endl;
|
||||
std::cout << "-- pair writing to std::cout:" << std::endl;
|
||||
std::pair<myenum, myenum> pair = std::make_pair(myenum::red, myenum::blue);
|
||||
std::cout << pair << std::endl;
|
||||
|
||||
// read tests
|
||||
@ -184,7 +336,15 @@ int main(int argc,char **argv)
|
||||
#ifdef HAVE_HDF5
|
||||
ioTest<Hdf5Writer, Hdf5Reader>("iotest.h5", obj, "HDF5 (object) ");
|
||||
ioTest<Hdf5Writer, Hdf5Reader>("iotest.h5", vec, "HDF5 (vector of objects)");
|
||||
std::cout << "\n==== detailed Hdf5 tensor tests (Grid::EigenIO)" << std::endl;
|
||||
TensorIO::Test<Hdf5Writer, Hdf5Reader>(".h5");
|
||||
#endif
|
||||
std::cout << "\n==== detailed binary tensor tests (Grid::EigenIO)" << std::endl;
|
||||
TensorIO::Test<BinaryWriter, BinaryReader>(".bin");
|
||||
std::cout << "\n==== detailed xml tensor tests (Grid::EigenIO)" << std::endl;
|
||||
TensorIO::Test<XmlWriter, XmlReader>(".xml", 6);
|
||||
std::cout << "\n==== detailed text tensor tests (Grid::EigenIO)" << std::endl;
|
||||
TensorIO::Test<TextWriter, TextReader>(".dat", 5);
|
||||
|
||||
std::cout << "\n==== vector flattening/reconstruction" << std::endl;
|
||||
typedef std::vector<std::vector<std::vector<double>>> vec3d;
|
||||
@ -227,4 +387,6 @@ int main(int argc,char **argv)
|
||||
tensorConvTest(rng, ColourVector);
|
||||
tensorConvTest(rng, SpinMatrix);
|
||||
tensorConvTest(rng, SpinVector);
|
||||
|
||||
Grid_finalize();
|
||||
}
|
||||
|
Reference in New Issue
Block a user