QMCPACK
RandomNumberControl.cpp
Go to the documentation of this file.
1 //////////////////////////////////////////////////////////////////////////////////////
2 // This file is distributed under the University of Illinois/NCSA Open Source License.
3 // See LICENSE file in top directory for details.
4 //
5 // Copyright (c) 2016 Jeongnim Kim and QMCPACK developers.
6 //
7 // File developed by: Miguel Morales, moralessilva2@llnl.gov, Lawrence Livermore National Laboratory
8 // Jeremy McMinnis, jmcminis@gmail.com, University of Illinois at Urbana-Champaign
9 // Cynthia Gu, zg1@ornl.gov, Oak Ridge National Laboratory
10 // Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
11 // Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
12 //
13 // File created by: Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
14 //////////////////////////////////////////////////////////////////////////////////////
15 
16 
17 #include <Configuration.h>
18 #include "Concurrency/OpenMP.h"
19 #include "OhmmsData/AttributeSet.h"
20 #include "RandomNumberControl.h"
21 #include "Utilities/Timer.h"
22 #include "hdf/HDFVersion.h"
23 #include "hdf/hdf_archive.h"
24 #include "mpi/collectives.h"
25 #include "Utilities/SimpleParser.h"
26 #include "OhmmsData/Libxml2Doc.h"
27 
28 namespace qmcplusplus
29 {
30 ///initialize the static data members
32 UPtrVector<RandomBase<QMCTraits::FullPrecRealType>> RandomNumberControl::Children;
34 
35 /// constructors and destructors
37  : OhmmsElementBase(aname), NeverBeenInitialized(true), myCur(NULL) //, Offset(5)
38 {}
39 
40 /// generic output
41 bool RandomNumberControl::get(std::ostream& os) const
42 {
43  if (omp_get_max_threads() > 1)
44  {
45  for (int ip = 0; ip < omp_get_max_threads(); ip++)
46  {
47  Children[ip]->write(os);
48  os << std::endl;
49  }
50  }
51  else
52  {
53  Random.write(os);
54  }
55  return true;
56 }
57 
58 /// generic input
59 bool RandomNumberControl::put(std::istream& is) { return true; }
60 
61 /// reset the generator
63 
64 /// reset the generator
66 {
67  int pid = OHMMS::Controller->rank();
68  int nprocs = OHMMS::Controller->size();
69  uint_type iseed = static_cast<uint_type>(std::time(0)) % 1024;
71  //OHMMS::Controller->bcast(iseed);//broadcast the seed
72  Offset = iseed;
73  std::vector<uint_type> mySeeds;
75  Random.init(mySeeds[pid]);
76  //change children as well
77  make_children();
78 }
79 
81 {
82  int nthreads = omp_get_max_threads();
83  int n = nthreads - Children.size();
84  while (n)
85  {
86  Children.push_back(std::make_unique<RandomGenerator>());
87  n--;
88  }
89  int rank = OHMMS::Controller->rank();
90  int nprocs = OHMMS::Controller->size();
91  int baseoffset = Offset + nprocs + nthreads * rank;
92  std::vector<uint_type> myprimes;
93  PrimeNumbers.get(baseoffset, nthreads, myprimes);
94  for (int ip = 0; ip < nthreads; ip++)
95  Children[ip]->init(myprimes[ip]);
96 }
97 
98 xmlNodePtr RandomNumberControl::initialize(xmlXPathContextPtr acontext)
99 {
100  OhmmsXPathObject rg_request("//random", acontext);
101  put(rg_request[0]);
102  return myCur;
103 }
104 
106 {
107  /* Add random number generator tester
108  */
109  int nthreads = omp_get_max_threads();
110  std::vector<double> avg(nthreads), avg2(nthreads);
111 #pragma omp parallel for
112  for (int ip = 0; ip < nthreads; ++ip)
113  {
114  const int n = 1000000;
115  double sum = 0.0, sum2 = 0.0;
116  auto& myrand(*Children[ip]);
117  for (int i = 0; i < n; ++i)
118  {
119  double r = myrand();
120  sum += r;
121  sum2 += r * r;
122  }
123  avg[ip] = sum / static_cast<double>(n);
124  avg2[ip] = sum2 / static_cast<double>(n);
125  }
126  std::vector<double> avg_tot(nthreads * OHMMS::Controller->size()), avg2_tot(nthreads * OHMMS::Controller->size());
127  mpi::gather(*OHMMS::Controller, avg, avg_tot);
128  mpi::gather(*OHMMS::Controller, avg2, avg2_tot);
129  double avg_g = 0.0;
130  double avg2_g = 0.0;
131  for (int i = 0, ii = 0; i < OHMMS::Controller->size(); ++i)
132  {
133  for (int ip = 0; ip < nthreads; ++ip, ++ii)
134  {
135  app_log() << "RNGTest " << std::setw(4) << i << std::setw(4) << ip << std::setw(20) << avg_tot[ii]
136  << std::setw(20) << avg2_tot[ii] - avg_tot[ii] * avg_tot[ii] << std::endl;
137  avg_g += avg_tot[ii];
138  avg2_g += avg2_tot[ii];
139  }
140  }
141  avg_g /= static_cast<double>(nthreads * OHMMS::Controller->size());
142  avg2_g /= static_cast<double>(nthreads * OHMMS::Controller->size());
143  app_log() << "RNGTest " << std::setw(4) << OHMMS::Controller->size() << std::setw(4) << nthreads << std::setw(20)
144  << avg_g << std::setw(20) << avg2_g - avg_g * avg_g << std::endl;
145  app_log().flush();
146 }
147 
148 bool RandomNumberControl::put(xmlNodePtr cur)
149 {
151  {
152  bool init_mpi = true;
153  int offset_in = -1; // default is to generate by Wall-clock
154  if (cur != NULL)
155  {
156  std::string pname("yes");
157  OhmmsAttributeSet oAttrib;
158  oAttrib.add(pname, "parallel");
159  oAttrib.add(offset_in, "seed");
160  oAttrib.put(cur);
161  if (pname == "0" || pname == "false" || pname == "no")
162  init_mpi = false;
163  }
164  int nprocs = 1;
165  int pid = 0;
166  if (init_mpi)
167  {
168  pid = OHMMS::Controller->rank();
169  nprocs = OHMMS::Controller->size();
170  }
171 
172  app_summary() << std::endl;
173  app_summary() << " Random Number" << std::endl;
174  app_summary() << " -------------" << std::endl;
175  if (offset_in < 0)
176  {
177  offset_in = static_cast<int>(static_cast<uint_type>(std::time(0)) % 1024);
178  app_summary() << " Offset for the random number seeds based on time: " << offset_in << std::endl;
179  mpi::bcast(*OHMMS::Controller, offset_in);
180  }
181  else
182  {
183  offset_in %= 1024;
184  app_summary() << " Offset for the random number seeds from input file (mod 1024): " << offset_in << std::endl;
185  }
186  app_summary() << std::endl;
187  Offset = offset_in;
188  std::vector<uint_type> mySeeds;
189  //allocate twice of what is required
190  PrimeNumbers.get(Offset, nprocs * (omp_get_max_threads() + 2), mySeeds);
191  Random.init(mySeeds[pid]);
192  app_log() << " Range of prime numbers to use as seeds over processors and threads = " << mySeeds[0] << "-"
193  << mySeeds[nprocs * omp_get_max_threads()] << std::endl;
194  app_log() << std::endl;
195 
196  make_children();
197  NeverBeenInitialized = false;
198  }
199  else
200  reset();
201  return true;
202 }
203 
204 /*New functions past this point*/
205 //switch between read functions
206 void RandomNumberControl::read(const std::string& fname, Communicate* comm)
207 {
208  std::string h5name = fname + ".random.h5";
209  hdf_archive hin(comm, true); //attempt to read in parallel
210  hin.open(h5name, H5F_ACC_RDONLY);
211  if (hin.is_parallel())
212  read_parallel(hin, comm);
213  else
214  read_rank_0(hin, comm);
215 }
216 
217 void RandomNumberControl::write(const std::string& fname, Communicate* comm)
218 {
220 }
221 
222 //switch between write functions
224  const std::string& fname,
225  Communicate* comm)
226 {
227  std::string h5name = fname + ".random.h5";
228  hdf_archive hout(comm, true); //attempt to write in parallel
229  hout.create(h5name);
230  if (hout.is_parallel())
231  write_parallel(rng, hout, comm);
232  else
233  write_rank_0(rng, hout, comm);
234 }
235 
236 //Parallel read
238 {
239  // cast integer to size_t
240  const size_t nthreads = static_cast<size_t>(omp_get_max_threads());
241  const size_t comm_size = static_cast<size_t>(comm->size());
242  const size_t comm_rank = static_cast<size_t>(comm->rank());
243 
244  std::vector<uint_type> vt, mt;
245  TinyVector<int, 3> shape_now(comm->size(), nthreads, Random.state_size()); //cur configuration
246  TinyVector<int, 3> shape_hdf5(3, 0); //configuration when file was written
247 
248  //grab shape and Random.state_size() used to create hdf5 file
249  hin.push(hdf::main_state);
250  hin.read(shape_hdf5, "nprocs_nthreads_statesize");
251 
252  //if hdf5 file's shape and the current shape don't match, abort read
253  if (shape_hdf5[0] != shape_now[0] || shape_hdf5[1] != shape_now[1] || shape_hdf5[2] != shape_now[2])
254  {
255  app_log() << "Mismatched random number generators."
256  << "\n Number of procs in streams : old=" << shape_hdf5[0] << " new= " << shape_now[0]
257  << "\n Number of threads in streams : old=" << shape_hdf5[1] << " new= " << shape_now[1]
258  << "\n State size per stream : old=" << shape_hdf5[2] << " new= " << shape_now[2]
259  << "\n Using the random streams generated at the initialization.\n";
260  return;
261  }
262  app_log() << " Restart from the random number streams from the previous configuration.\n";
263 
264  vt.resize(nthreads * Random.state_size()); //buffer for children[ip]
265  mt.resize(Random.state_size()); //buffer for single thread Random object of random nums
266 
267  std::array<size_t, 2> shape{comm_size * nthreads, Random.state_size()}; //global dims of children dataset
268  std::array<size_t, 2> counts{nthreads, Random.state_size()}; //local dimensions of dataset
269  std::array<size_t, 2> offsets{comm_rank * nthreads, 0}; //offsets for each process to read in
270 
271  hin.push("random"); //group that holds children[ip] random nums
272  hyperslab_proxy<std::vector<uint_type>, 2> slab(vt, shape, counts, offsets);
273  hin.read(slab, Random.EngineName);
274 
275  hin.pop();
276  hin.push("random_master"); //group that holds Random_th random nums
277  shape[0] = comm->size(); //reset shape, counts and offset for non-multiple threads
278  counts[0] = 1;
279  offsets[0] = comm->rank();
280  hyperslab_proxy<std::vector<uint_type>, 2> slab2(mt, shape, counts, offsets);
281  hin.read(slab2, Random.EngineName);
282  hin.close();
283 
284  std::vector<uint_type>::iterator vt_it(vt.begin());
285  for (int ip = 0; ip < nthreads; ip++, vt_it += shape[1])
286  {
287  std::vector<uint_type> c(vt_it, vt_it + shape[1]);
288  Children[ip]->load(c); //load random nums back to program from buffer
289  }
290  Random.load(mt); //load random nums back to prog from buffer
291 }
292 
293 //Parallel write
295  hdf_archive& hout,
296  Communicate* comm)
297 {
298  // cast integer to size_t
299  const size_t nthreads = static_cast<size_t>(omp_get_max_threads());
300  const size_t comm_size = static_cast<size_t>(comm->size());
301  const size_t comm_rank = static_cast<size_t>(comm->rank());
302 
303  std::vector<uint_type> vt, mt;
304  TinyVector<int, 3> shape_hdf5(comm->size(), nthreads, Random.state_size()); //configuration at write time
305  vt.reserve(nthreads * Random.state_size()); //buffer for random numbers from children[ip] of each thread
306  mt.reserve(Random.state_size()); //buffer for random numbers from single Random object
307 
308  std::vector<uint_type> c;
309  for (int ip = 0; ip < nthreads; ++ip)
310  {
311  rng[ip].get().save(c);
312  vt.insert(vt.end(), c.begin(), c.end()); //get nums from each thread into buffer
313  }
314  Random.save(mt); //get nums for single random object (no threads)
315 
316  std::array<size_t, 2> shape{comm_size * nthreads, Random.state_size()}; //global dimensions
317  std::array<size_t, 2> counts{nthreads, Random.state_size()}; //local dimensions
318  std::array<size_t, 2> offsets{comm_rank * nthreads, 0}; //offset for the file write
319 
320  hout.push(hdf::main_state);
321  hout.write(shape_hdf5, "nprocs_nthreads_statesize"); //save the shape of the data at write
322 
323  hout.push("random"); //group for children[ip]
324  hyperslab_proxy<std::vector<uint_type>, 2> slab(vt, shape, counts, offsets);
325  hout.write(slab, Random.EngineName); //write to hdf5file
326  hout.pop();
327 
328  shape[0] = comm->size(); //adjust shape, counts, offset for just one thread
329  counts[0] = 1;
330  offsets[0] = comm->rank();
331  hout.push("random_master"); //group for random object without threads
332  hyperslab_proxy<std::vector<uint_type>, 2> slab2(mt, shape, counts, offsets);
333  hout.write(slab2, Random.EngineName); //write data to hdf5 file
334  hout.close();
335 }
336 
337 //Scatter read
339 {
340  // cast integer to size_t
341  const size_t nthreads = static_cast<size_t>(omp_get_max_threads());
342  const size_t comm_size = static_cast<size_t>(comm->size());
343  const size_t comm_rank = static_cast<size_t>(comm->rank());
344 
345  std::vector<uint_type> vt, vt_tot, mt, mt_tot;
346  TinyVector<size_t, 3> shape_now(comm_size, nthreads, Random.state_size()); //current configuration
347  TinyVector<size_t, 3> shape_hdf5; //configuration when hdf5 file was written
348  std::array<size_t, 2> shape{comm_size * nthreads, Random.state_size()}; //dimensions of children dataset
349 
350  //grab configuration of threads/procs and Random.state_size() in hdf5 file
351  if (comm->rank() == 0)
352  {
353  hin.push(hdf::main_state);
354  hin.read(shape_hdf5, "nprocs_nthreads_statesize");
355  }
356 
357  mpi::bcast(*comm, shape_hdf5);
358 
359  //if hdf5 file's configuration and current configuration don't match, abort read
360  if (shape_hdf5[0] != shape_now[0] || shape_hdf5[1] != shape_now[1] || shape_hdf5[2] != shape_now[2])
361  {
362  app_log() << "Mismatched random number generators."
363  << "\n Number of procs in streams : old=" << shape_hdf5[0] << " new= " << shape_now[0]
364  << "\n Number of threads in streams : old=" << shape_hdf5[1] << " new= " << shape_now[1]
365  << "\n State size per stream : old=" << shape_hdf5[2] << " new= " << shape_now[2]
366  << "\n Using the random streams generated at the initialization.\n";
367  return;
368  }
369  app_log() << " Restart from the random number streams from the previous configuration.\n";
370 
371  vt.resize(nthreads * Random.state_size()); //buffer for random nums in children of each thread
372  mt.resize(Random.state_size()); //buffer for random numbers from single Random object
373 
374  if (comm->rank() == 0)
375  {
376  hin.push("random"); //group for children[ip] (Random.object for each thread)
377  vt_tot.resize(nthreads * Random.state_size() * comm->size());
378  hin.readSlabReshaped(vt_tot, shape, Random.EngineName);
379  hin.pop();
380 
381  shape[0] = comm->size(); //reset shape to one thread per process
382  mt_tot.resize(Random.state_size() * comm->size());
383  hin.push("random_master"); //group for single Random object
384  hin.readSlabReshaped(mt_tot, shape, Random.EngineName);
385  hin.close();
386  }
387 
388  if (comm->size() > 1)
389  {
390  mpi::scatter(*comm, vt_tot, vt); //divide big buffer into on for each proc
391  mpi::scatter(*comm, mt_tot, mt);
392  }
393  else
394  {
395  copy(vt_tot.begin(), vt_tot.end(), vt.begin());
396  copy(mt_tot.begin(), mt_tot.end(), mt.begin());
397  }
398 
399  std::vector<uint_type>::iterator vt_it(vt.begin());
400  for (int i = 0; i < nthreads; i++, vt_it += shape[1])
401  {
402  std::vector<uint_type> c(vt_it, vt_it + shape[1]);
403  Children[i]->load(c); //read seeds for each thread from buffer back into object
404  }
405  Random.load(mt); //read seeds back into object
406 }
407 
408 //scatter write
410  hdf_archive& hout,
411  Communicate* comm)
412 {
413  // cast integer to size_t
414  const size_t nthreads = static_cast<size_t>(omp_get_max_threads());
415  const size_t comm_size = static_cast<size_t>(comm->size());
416  const size_t comm_rank = static_cast<size_t>(comm->rank());
417 
418  std::vector<uint_type> vt, vt_tot, mt, mt_tot;
419  std::array<size_t, 2> shape{comm_size * nthreads, Random.state_size()}; //dimensions of children dataset
420  TinyVector<size_t, 3> shape_hdf5(comm_size, nthreads, Random.state_size()); //configuration at write time
421  vt.reserve(nthreads * Random.state_size()); //buffer for children[ip] (Random object of seeds for each thread)
422  mt.reserve(Random.state_size()); //buffer for single Random object of seeds, one per proc regardless of thread num
423 
424  for (int i = 0; i < nthreads; ++i)
425  {
426  std::vector<uint_type> c;
427  rng[i].get().save(c);
428  vt.insert(vt.end(), c.begin(), c.end()); //copy children[nthreads] seeds to buffer
429  }
430  Random.save(mt); //copy random_th seeds to buffer
431 
432  if (comm->size() > 1)
433  {
434  vt_tot.resize(vt.size() * comm->size());
435  mt_tot.resize(mt.size() * comm->size());
436  mpi::gather(*comm, vt, vt_tot); //gather into one big buffer for master write
437  mpi::gather(*comm, mt, mt_tot);
438  }
439  else
440  {
441  vt_tot = vt;
442  mt_tot = mt;
443  }
444 
445  if (comm->rank() == 0)
446  {
447  hout.push(hdf::main_state);
448  hout.write(shape_hdf5, "nprocs_nthreads_statesize"); //configuration at write time to file
449 
450  hout.push("random"); //group for children[ip]
451  hout.writeSlabReshaped(vt_tot, shape, Random.EngineName);
452  hout.pop();
453 
454  shape[0] = comm->size(); //reset dims for single thread use
455  hout.push("random_master"); //group for random_th object
456  hout.writeSlabReshaped(mt_tot, shape, Random.EngineName);
457  hout.close();
458  }
459 }
460 } // namespace qmcplusplus
static void read(const std::string &fname, Communicate *comm)
read in parallel or serial
class to generate prime numbers
void write(T &data, const std::string &aname)
write the data to the group aname and check status runtime error is issued on I/O error ...
Definition: hdf_archive.h:259
bool open(const std::filesystem::path &fname, unsigned flags=H5F_ACC_RDWR)
open a file
helper functions for EinsplineSetBuilder
Definition: Configuration.h:43
int rank() const
return the rank
Definition: Communicate.h:116
class to use file space hyperslab with a serialized container
Definition: hdf_hyperslab.h:35
static void read_parallel(hdf_archive &hin, Communicate *comm)
read random state from a hdf file in parallel
std::ostream & app_log()
Definition: OutputManager.h:65
std::ostream & app_summary()
Definition: OutputManager.h:63
bool put(xmlNodePtr cur)
assign attributes to the set
Definition: AttributeSet.h:55
static PrimeNumberSet< uint_type > PrimeNumbers
initialize the static data members
void close()
close all the open groups and file
Definition: hdf_archive.cpp:38
class to handle hdf file
Definition: hdf_archive.h:51
Timer class.
void readSlabReshaped(T &data, const std::array< IT, RANK > &shape, const std::string &aname)
read file dataset with a specific shape into a container and check status
Definition: hdf_archive.h:321
#define Random
Communicate * Controller
Global Communicator for a process.
Definition: Communicate.cpp:35
static void write(const std::string &fname, Communicate *comm)
write in parallel or serial
int size() const
return the number of tasks
Definition: Communicate.h:118
static RefVector< T > convertUPtrToRefVector(const UPtrVector< T > &ptr_list)
convert a vector of std::unique_ptrs<T> to a refvector<T>
class to handle xmlXPathObject
Definition: Libxml2Doc.h:26
bool is_parallel() const
return true if parallel i/o
Definition: hdf_archive.h:120
xmlNodePtr initialize(xmlXPathContextPtr)
void copy(const Array< T1, 3 > &src, Array< T2, 3 > &dest)
Definition: Blitz.h:639
Wrapping information on parallelism.
Definition: Communicate.h:68
void bcast(T &a, Communicate *comm)
Definition: CommUtilities.h:78
void reset() override
reset the generator
static void write_parallel(const RefVector< RandomBase< FullPrecRealType >> &rng, hdf_archive &hout, Communicate *comm)
write random state to a hdf file in parallel
omp_int_t omp_get_max_threads()
Definition: OpenMP.h:26
Abstract class to provide xml-compatible I/O interfaces for the derived classes.
void writeSlabReshaped(T &data, const std::array< IT, RANK > &shape, const std::string &aname)
write the container data with a specific shape and check status
Definition: hdf_archive.h:274
class to handle a set of attributes of an xmlNode
Definition: AttributeSet.h:24
RandomBase< FullPrecRealType >::uint_type uint_type
bool get(std::ostream &os) const override
generic output
const char main_state[]
Definition: HDFVersion.h:31
static UPtrVector< RandomBase< FullPrecRealType > > Children
void push(const std::string &gname, bool createit=true)
push a group to the group stack
std::vector< std::reference_wrapper< T > > RefVector
bool create(const std::filesystem::path &fname, unsigned flags=H5F_ACC_TRUNC)
create a file
bool put(std::istream &is) override
generic input
uint_fast32_t uint_type
Definition: RandomBase.h:28
void read(T &data, const std::string &aname)
read the data from the group aname and check status runtime error is issued on I/O error ...
Definition: hdf_archive.h:306
static void write_rank_0(const RefVector< RandomBase< FullPrecRealType >> &rng, hdf_archive &hout, Communicate *comm)
rank 0 gathers the random states from all the other ranks and write them to a hdf file ...
static void make_seeds()
reset the generator
void add(PDT &aparam, const std::string &aname, std::vector< PDT > candidate_values={}, TagStatus status=TagStatus::OPTIONAL)
add a new attribute
Definition: AttributeSet.h:42
bool get(UIntType offset, int n, std::vector< UIntType > &primes_add)
add n new primes starting with an offset
RandomNumberControl(const char *aname="random")
constructors and destructors
static void read_rank_0(hdf_archive &hin, Communicate *comm)
rank 0 reads random states from a hdf file and distributes them to all the other ranks ...