RepastHPC  2.3.1
Random.h
1 /*
2  * Repast for High Performance Computing (Repast HPC)
3  *
4  * Copyright (c) 2010 Argonne National Laboratory
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with
8  * or without modification, are permitted provided that the following
9  * conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice,
12  * this list of conditions and the following disclaimer.
13  *
14  * Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution.
17  *
18  * Neither the name of the Argonne National Laboratory nor the names of its
19  * contributors may be used to endorse or promote products derived from
20  * this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR
26  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
32  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  *
35  * Random.h
36  *
37  * Created on: Jun 9, 2009
38  * Author: nick
39  */
40 
41 #ifndef RANDOM_H_
42 #define RANDOM_H_
43 
44 #include <vector>
45 #include <set>
46 #include <map>
47 #include <string>
48 
49 
50 #include <boost/random/variate_generator.hpp>
51 #include <boost/random/uniform_int.hpp>
52 #include <boost/random/uniform_real.hpp>
53 #include <boost/random/triangle_distribution.hpp>
54 #include <boost/random/cauchy_distribution.hpp>
55 #include <boost/random/exponential_distribution.hpp>
56 #include <boost/random/geometric_distribution.hpp>
57 #include <boost/random/normal_distribution.hpp>
58 #include <boost/random/lognormal_distribution.hpp>
59 #include <boost/random/mersenne_twister.hpp>
60 #include <boost/cstdint.hpp>
61 
62 
63 namespace repast {
64 
65 typedef boost::variate_generator<boost::mt19937&, boost::uniform_real<> > _RealUniformGenerator;
66 typedef boost::variate_generator<boost::mt19937&, boost::uniform_int<> > _IntUniformGenerator;
67 
68 typedef boost::variate_generator<boost::mt19937&, boost::triangle_distribution<> > _TriangleGenerator;
69 typedef boost::variate_generator<boost::mt19937&, boost::cauchy_distribution<> > _CauchyGenerator;
70 typedef boost::variate_generator<boost::mt19937&, boost::exponential_distribution<> > _ExponentialGenerator;
71 typedef boost::variate_generator<boost::mt19937&, boost::geometric_distribution<boost::uniform_real<> > >
72  _GeometricGenerator;
73 typedef boost::variate_generator<boost::mt19937&, boost::normal_distribution<> > _NormalGenerator;
74 typedef boost::variate_generator<boost::mt19937&, boost::lognormal_distribution<> > _LogNormalGenerator;
75 
80 
81 public:
82  virtual ~NumberGenerator() {
83  }
84 
89  virtual double next() = 0;
90 };
91 
96 template<typename T>
98 
99 private:
100  T gen;
101 
102 public:
103  DefaultNumberGenerator(T generator);
104  double next();
105 };
106 
107 template<typename T>
109  gen(generator) {
110 }
111 
112 template<typename T>
114  return gen();
115 }
116 
124 
128 class Random {
129 
130 private:
131  static Random* instance_;
132 
133  boost::mt19937 rng;
134  _RealUniformGenerator uniGen;
135 
136  std::map<std::string, NumberGenerator*> generators;
137 
138 protected:
139  Random(boost::uint32_t seed);
140  Random(boost::mt19937 generator);
141 
142 public:
143 
149  static void initialize(boost::uint32_t seed);
150 
156  static void initialize(boost::mt19937 generator);
157 
161  static Random* instance();
162  virtual ~Random();
163 
171  void putGenerator(const std::string& id, NumberGenerator* generator);
172 
179  NumberGenerator* getGenerator(const std::string& id);
180 
186  boost::mt19937& engine() {
187  return rng;
188  }
189 
205  double nextDouble();
206 
215  DoubleUniformGenerator createUniDoubleGenerator(double from, double to);
216 
225  IntUniformGenerator createUniIntGenerator(int from, int to);
226 
238  TriangleGenerator createTriangleGenerator(double lowerBound, double mostLikely, double upperBound);
239 
248  CauchyGenerator createCauchyGenerator(double median, double sigma);
249 
258 
266  NormalGenerator createNormalGenerator(double mean, double sigma);
267 
272  LogNormalGenerator createLogNormalGenerator(double mean, double sigma);
273 };
274 
282 ptrdiff_t uni_random(ptrdiff_t i);
283 
294 template<typename I>
295 int countOf(I iteratorStart, I iteratorEnd){
296  I iterator = iteratorStart;
297  int c = 0;
298  while(iterator != iteratorEnd){ iterator++; c++; }
299  return c;
300 }
301 
317 template<typename T>
318 void shuffleList(std::vector<T*>& elementList){
319  if(elementList.size() <= 1) return;
320  IntUniformGenerator rnd = Random::instance()->createUniIntGenerator(0, elementList.size() - 1);
321  T* swap;
322  for(size_t i = 0, sz = elementList.size(); i < sz; i++){
323  int other = rnd.next();
324  swap = elementList[i];
325  elementList[i] = elementList[other];
326  elementList[other] = swap;
327  }
328 
329  // OR
330  // see: http://en.wikipedia.org/wiki/Fisher-Yates_shuffle#The_modern_algorithm
331 
332  // DoubleUniformGenerator rnd = Random::instance()->createUniDoubleGenerator(0, 1);
333  // T* swap;
334  // for(int pos = elementList.size() - 1; pos > 0; pos--){
335  // int range = pos + 1;
336  // int other = (int)(rnd.next() * (range));
337  // swap = elementList[pos];
338  // elementList[pos] = elementList[other];
339  // elementList[other] = swap;
340  // }
341 }
342 
351 template<typename T>
352 void shuffleSet(std::set<T*>& elementSet, std::vector<T*>& elementList){
353  elementList.assign(elementSet.begin(), elementSet.end());
354  shuffleList(elementList);
355 
356  // // Or- probably faster... ??
357  //
358  // elementList.reserve(elementSet.size());
359  // typename std::set<T*>::iterator setIterator = elementSet.begin();
360  // DoubleUniformGenerator rnd = Random::instance()->createUniDoubleGenerator(0,1);
361  // for(int i = 0; i < elementSet.size(); i++){
362  // int j = (int)(rnd.next() * (i + 1));
363  // elementList.push_back(elementList[j]);
364  // elementList[j] = *setIterator;
365  // setIterator++;
366  // }
367 }
368 
404 template<typename I>
406 private:
407  I it;
408  I begin;
409 
410  int interval;
411  int maxLandmark;
412  std::vector<std::pair<int, I > > landmarks;
413 
414 public:
415 
422  RandomAccess(I beginning, int size) :
423  interval((int)(sqrt((double)size))), maxLandmark(0), it(beginning), begin(beginning) {
424  landmarks.push_back(std::pair<int, I >(0, beginning));
425  }
426 
432  I get(int index){
433  bool place = (index > (maxLandmark + interval));
434  typename std::vector<std::pair<int, I > >::iterator lm = landmarks.end();
435  while((--lm)->first > index);
436  int c = lm->first;
437  it = lm->second;
438  if(place){
439  while(c != index){
440  c++;
441  it++;
442  if(c % interval == 0){
443  landmarks.push_back(std::pair<int, I >(c, it));
444  maxLandmark = c;
445  }
446  }
447  }
448  else{
449  while(c != index){
450  c++;
451  it++;
452  }
453  }
454  return it;
455  }
456 
457 };
458 
459 
492 template<typename T, typename I>
493 void selectNElementsAtRandom(I iterator, int size, unsigned int count, std::set<T*>&selectedElements, bool remove = false){
494  // When 'selectedElements' is large in comparison to size, or 'count' is
495  // large in comparison to size, or both, a potential problem arises; this is both
496  // a performance issue and a potential error (infinite loop). The number
497  // of valid selections remaining in the original population will be
498  // the size of the population minus the number of members of the original
499  // population already in the set of 'selectedElements'. There is a performance
500  // problem that can arise if the number of valid selections approaches
501  // zero, and an infinite loop arises if that number reaches zero. However, a
502  // complication is that we do not assume that all members in
503  // 'selectedElements' are drawn from the same population that 'iterator' describes.
504 
505  // At a general level, the solution to this is that when the number of valid
506  // selections is expected to become small, the algorithm will switch to
507  // selecting the elements that will _not_ be added to the final set, rather
508  // than those that will.
509 
510  // However, the more immediate issue is determining whether the problem
511  // will arise at all, and a key question is how many members of 'selectedElements'
512  // are drawn from the population. This, however, is costly to check, so
513  // it should only be checked if it seems possible to lead to a problem.
514 
515  // A ceiling; the actual value may be lower
516  int maxAlreadySelected = selectedElements.size();
517 
518  // We are not certain _any_ elements in the selectedElements set are from this population
519  int knownSelectedElementsFromPop = 0;
520 
521  // This is a floor; there are at least this many available, but possibly more
522  int minAvailable = size - maxAlreadySelected;
523 
524  // If you are requesting more than might be available, you must determine
525  // the actual number available.
526  if(count > minAvailable){
527  if(maxAlreadySelected > 0){
528  I tempIt = iterator;
529  for(int i = 0; i < size; i++){
530  T* ptr = (&**tempIt);
531  typename std::set<T*>::iterator found = selectedElements.find(ptr);
532  if(found != selectedElements.end()) knownSelectedElementsFromPop++;
533  tempIt++;
534  }
535  maxAlreadySelected = knownSelectedElementsFromPop;
536  minAvailable = size - maxAlreadySelected;
537  }
538  }
539  // If removing the original elements, will need a copy of them
540  typename std::vector<T*> tempToRemove;
541  if(remove){
542  tempToRemove.assign(selectedElements.begin(), selectedElements.end());
543  }
544 
545  // There is no way to satisfy the request; copy all and return
546  if(count > minAvailable){
547  I it = iterator;
548  for(int i = 0; i < size; i++){
549  selectedElements.insert(&**it);
550  it++;
551  }
552  }
553  else{
554  RandomAccess<I> ra(iterator, size);
555  IntUniformGenerator rnd = Random::instance()->createUniIntGenerator(0, size - 1);
556  T* ptr;
557 
558  // If the count of elements is very high in proportion to the population, faster to
559  // choose agents that will _not_ be in the final set and then switch...
560  // First the normal case, in which the number of agents is low
561  if(count <= ((double)minAvailable)*0.6){ // Tests suggest that 2/3 is probably too high, so adjusted to .6; TODO optimize or analytically solve
562  for (unsigned int i = 0; i < count; i++)
563  do { ptr = &**ra.get(rnd.next()); } while(!(selectedElements.insert(ptr).second));
564  }
565  else{ // Now the other case
566  std::set<T*> elementsThatWillNotBeAdded;
567 
568  if(selectedElements.size() > 0){
569  if(selectedElements.size() == knownSelectedElementsFromPop){ // Maybe we already checked and they ALL belong
570  elementsThatWillNotBeAdded.insert(selectedElements.begin(), selectedElements.end());
571  }
572  else{
573  I tempIt = iterator;
574  for(int i = 0; i < size; i++){
575  ptr = (&**tempIt);
576  if(selectedElements.find(ptr) != selectedElements.end()) elementsThatWillNotBeAdded.insert(ptr);
577  tempIt++;
578  }
579  }
580  }
581  // Note: duplicate element will not be inserted; failure will leave size of elementsThatWillNotBeAdded unchanged.
582  while((size - elementsThatWillNotBeAdded.size()) > count) elementsThatWillNotBeAdded.insert(&**ra.get(rnd.next()));
583 
584  // Once the set of elements that will not be added is complete, cycle through the iterator and add
585  // all of those elements that are not in elementsThatWillNotBeAdded
586  I toAdd = iterator;
587  typename std::set<T*>::iterator notFound = elementsThatWillNotBeAdded.end();
588  for(int i = 0; i < size; i++){
589  ptr = &**toAdd;
590  if(elementsThatWillNotBeAdded.find(ptr) == notFound) selectedElements.insert(ptr);
591  toAdd++;
592  }
593  }
594  }
595  // Before returning, remove the elements from the set, if the user requested
596  if(remove){
597  typename std::vector<T*>::iterator toRemove = tempToRemove.begin();
598  while(toRemove != tempToRemove.end()){
599  selectedElements.erase(*toRemove);
600  toRemove++;
601  }
602  }
603 }
604 
605 
628 template<typename T, typename I>
629 void selectNElementsAtRandom(I iteratorStart, I iteratorEnd, int count, std::set<T*>&selectedElements, bool remove = false){
630  selectNElementsAtRandom(iteratorStart, countOf(iteratorStart, iteratorEnd), count, selectedElements, remove);
631 }
632 
662 template<typename T, typename I>
663 void selectNElementsInRandomOrder(I iterator, int size, int count, std::vector<T*>& selectedElements, bool remove = false){
664  // Transfer all elements from the vector to a set
665  std::set<T*> selectedElementSet;
666  selectedElementSet.insert(selectedElements.begin(), selectedElements.end());
667  selectedElements.clear();
668  selectNElementsAtRandom(iterator, size, count, selectedElementSet, remove);
669  shuffleSet(selectedElementSet, selectedElements);
670 }
671 
701 template<typename T, typename I>
702 void selectNElementsInRandomOrder(I iteratorStart, I iteratorEnd, int count, std::vector<T*>& selectedElements, bool remove = false){
703  selectNElementsInRandomOrder(iteratorStart, countOf(iteratorStart, iteratorEnd), count, selectedElements, remove);
704 }
705 
706 
707 }
708 
709 #endif /* RANDOM_H_ */
repast::Random::createLogNormalGenerator
LogNormalGenerator createLogNormalGenerator(double mean, double sigma)
Produces random numbers with p(x) = 1/(x * normal_sigma * sqrt(2*pi)) * exp( -(log(x)-normal_mean)2 /...
Definition: Random.cpp:138
repast::Random::createUniDoubleGenerator
DoubleUniformGenerator createUniDoubleGenerator(double from, double to)
Creates a generator that produces doubles in the range [from, to).
Definition: Random.cpp:90
repast::Random::nextDouble
double nextDouble()
Gets the current seed.
Definition: Random.cpp:96
repast::Random::putGenerator
void putGenerator(const std::string &id, NumberGenerator *generator)
Puts the named generator into this Random.
Definition: Random.cpp:152
repast::Random::createTriangleGenerator
TriangleGenerator createTriangleGenerator(double lowerBound, double mostLikely, double upperBound)
Creates a triangle generator with the specified properties.
Definition: Random.cpp:106
repast::Random::instance
static Random * instance()
Gets the singleton instance of this Random.
Definition: Random.cpp:80
repast::Random::initialize
static void initialize(boost::uint32_t seed)
Initialize the Random singleton with the specified seed.
repast::DefaultNumberGenerator
Adapts the templated boost::variate_generator to the NumberGenerator interface.
Definition: Random.h:97
repast::Random::createCauchyGenerator
CauchyGenerator createCauchyGenerator(double median, double sigma)
pdf: p(x) = sigma/(pi*(sigma**2 + (x-median)**2))
Definition: Random.cpp:112
repast::RandomAccess::RandomAccess
RandomAccess(I beginning, int size)
Constructs a RandomAccess instance for this iterator.
Definition: Random.h:422
repast::Random::getGenerator
NumberGenerator * getGenerator(const std::string &id)
Gets the named generator or 0 if the name is not found.
Definition: Random.cpp:156
repast::DefaultNumberGenerator::next
double next()
Gets the "next" number from this Number Generator.
Definition: Random.h:113
repast::RandomAccess::get
I get(int index)
Gets the element at the specified index.
Definition: Random.h:432
repast::Random
Methods for working with random distributions, draws etc.
Definition: Random.h:128
repast::Random::createExponentialGenerator
ExponentialGenerator createExponentialGenerator(double lambda)
pdf: p(x) = lambda * exp(-lambda * x)
Definition: Random.cpp:118
repast::Random::engine
boost::mt19937 & engine()
Gets the random number engine from which the distributions are created.
Definition: Random.h:186
repast::NumberGenerator::next
virtual double next()=0
Gets the "next" number from this Number Generator.
repast::NumberGenerator
Number generator interface.
Definition: Random.h:79
repast::Random::createUniIntGenerator
IntUniformGenerator createUniIntGenerator(int from, int to)
Creates a generator that produces ints in the range [from, to].
Definition: Random.cpp:100
repast::Random::createNormalGenerator
NormalGenerator createNormalGenerator(double mean, double sigma)
Creates a normal generator.
Definition: Random.cpp:132
repast::RandomAccess
Given an iterator and a number of elements, creates a data structure that allows efficient access to ...
Definition: Random.h:405