1 #ifndef UTILS_RANDOM_H_
2 #define UTILS_RANDOM_H_
3 
4 #include <Eigen/Core>
5 #include "../Config.h"
6 #include "../RNG.h"
7 
8 namespace MiniDNN
9 {
10 
11 namespace internal
12 {
13 
14 
15 // Shuffle the integer array
shuffle(int * arr,const int n,RNG & rng)16 inline void shuffle(int* arr, const int n, RNG& rng)
17 {
18     for (int i = n - 1; i > 0; i--)
19     {
20         // A random non-negative integer <= i
21         const int j = int(rng.rand() * (i + 1));
22         // Swap arr[i] and arr[j]
23         const int tmp = arr[i];
24         arr[i] = arr[j];
25         arr[j] = tmp;
26     }
27 }
28 
29 template <typename DerivedX, typename DerivedY, typename XType, typename YType>
create_shuffled_batches(const Eigen::MatrixBase<DerivedX> & x,const Eigen::MatrixBase<DerivedY> & y,int batch_size,RNG & rng,std::vector<XType> & x_batches,std::vector<YType> & y_batches)30 inline int create_shuffled_batches(
31     const Eigen::MatrixBase<DerivedX>& x, const Eigen::MatrixBase<DerivedY>& y,
32     int batch_size, RNG& rng,
33     std::vector<XType>& x_batches, std::vector<YType>& y_batches
34 )
35 {
36     const int nobs = x.cols();
37     const int dimx = x.rows();
38     const int dimy = y.rows();
39 
40     if (y.cols() != nobs)
41     {
42         throw std::invalid_argument("Input X and Y have different number of observations");
43     }
44 
45     // Randomly shuffle the IDs
46     Eigen::VectorXi id = Eigen::VectorXi::LinSpaced(nobs, 0, nobs - 1);
47     shuffle(id.data(), id.size(), rng);
48 
49     // Compute batch size
50     if (batch_size > nobs)
51     {
52         batch_size = nobs;
53     }
54 
55     const int nbatch = (nobs - 1) / batch_size + 1;
56     const int last_batch_size = nobs - (nbatch - 1) * batch_size;
57     // Create shuffled data
58     x_batches.clear();
59     y_batches.clear();
60     x_batches.reserve(nbatch);
61     y_batches.reserve(nbatch);
62 
63     for (int i = 0; i < nbatch; i++)
64     {
65         const int bsize = (i == nbatch - 1) ? last_batch_size : batch_size;
66         x_batches.push_back(XType(dimx, bsize));
67         y_batches.push_back(YType(dimy, bsize));
68         // Copy data
69         const int offset = i * batch_size;
70 
71         for (int j = 0; j < bsize; j++)
72         {
73             x_batches[i].col(j).noalias() = x.col(id[offset + j]);
74             y_batches[i].col(j).noalias() = y.col(id[offset + j]);
75         }
76     }
77 
78     return nbatch;
79 }
80 
81 // Fill array with N(mu, sigma^2) random numbers
82 inline void set_normal_random(Scalar* arr, const int n, RNG& rng,
83                               const Scalar& mu = Scalar(0),
84                               const Scalar& sigma = Scalar(1))
85 {
86     // For simplicity we use Box-Muller transform to generate normal random variates
87     const double two_pi = 6.283185307179586476925286766559;
88 
89     for (int i = 0; i < n - 1; i += 2)
90     {
91         const double t1 = sigma * std::sqrt(-2 * std::log(rng.rand()));
92         const double t2 = two_pi * rng.rand();
93         arr[i]     = t1 * std::cos(t2) + mu;
94         arr[i + 1] = t1 * std::sin(t2) + mu;
95     }
96 
97     if (n % 2 == 1)
98     {
99         const double t1 = sigma * std::sqrt(-2 * std::log(rng.rand()));
100         const double t2 = two_pi * rng.rand();
101         arr[n - 1] = t1 * std::cos(t2) + mu;
102     }
103 }
104 
105 
106 } // namespace internal
107 
108 } // namespace MiniDNN
109 
110 
111 #endif /* UTILS_RANDOM_H_ */
112