From fa64a25825aaa059f9b5f0d9fb57343c5c8989a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org> Date: Wed, 3 Apr 2019 18:49:32 +0200 Subject: [PATCH] qmc_sequence DLL: various modernizations and simplifications --- mex/sources/sobol/gaussian.hh | 67 +++++------- mex/sources/sobol/qmc_sequence.cc | 168 ++++++++++++------------------ mex/sources/sobol/sobol.hh | 66 ++++-------- 3 files changed, 114 insertions(+), 187 deletions(-) diff --git a/mex/sources/sobol/gaussian.hh b/mex/sources/sobol/gaussian.hh index d1fcd1b88a..fb44f589e1 100644 --- a/mex/sources/sobol/gaussian.hh +++ b/mex/sources/sobol/gaussian.hh @@ -22,18 +22,16 @@ ** AUTHOR(S): stephane DOT adjemian AT univ DASH lemans DOT fr */ -#include <cstdlib> -#include <iostream> -#include <iomanip> #include <cmath> -#include <ctime> +#include <limits> +#include <algorithm> #include <dynblas.h> using namespace std; -#define lb .02425 -#define ub .97575 +constexpr double lb = .02425; +constexpr double ub = .97575; #ifdef USE_OMP # include <omp.h> @@ -81,8 +79,8 @@ icdf(const T uniform) 2.445134137142996e+00, 3.754408661907416e+00 }; - T gaussian = (T) 0.0; - if ((0 < uniform) && (uniform < lb)) + T gaussian = static_cast<T>(0.0); + if (0 < uniform && uniform < lb) { T tmp; tmp = sqrt(-2*log(uniform)); @@ -90,7 +88,7 @@ icdf(const T uniform) } else { - if ((lb <= uniform) && (uniform <= ub)) + if (lb <= uniform && uniform <= ub) { T tmp, TMP; tmp = uniform - .5; @@ -99,7 +97,7 @@ icdf(const T uniform) } else { - if ((ub < uniform) && (uniform < 1)) + if (ub < uniform && uniform < 1) { T tmp; tmp = sqrt(-2*log(1-uniform)); @@ -107,7 +105,7 @@ icdf(const T uniform) } } } - if ((0 < uniform) && (uniform < 1)) + if (0 < uniform && uniform < 1) { T tmp, tmp_; tmp = .5*erfc(-gaussian/sqrt(2.0))-uniform; @@ -115,33 +113,29 @@ icdf(const T uniform) gaussian = gaussian - tmp_/(1+.5*gaussian*tmp_); } if (uniform == 0) - { - gaussian = -INFINITY; - } + gaussian = -numeric_limits<T>::infinity(); + if (uniform == 1) - { - gaussian = INFINITY; - } - return (gaussian); + gaussian = numeric_limits<T>::infinity(); + + return gaussian; } template<typename T> void -icdfm(const int n, T *U) +icdfm(int n, T *U) { #if USE_OMP # pragma omp parallel for num_threads(omp_get_num_threads()) #endif for (int i = 0; i < n; i++) - { - U[i] = icdf(U[i]); - } + U[i] = icdf(U[i]); return; } template<typename T> void -icdfmSigma(const int d, const int n, T *U, const double *LowerCholSigma) +icdfmSigma(int d, int n, T *U, const double *LowerCholSigma) { double one = 1.0; double zero = 0.0; @@ -150,13 +144,12 @@ icdfmSigma(const int d, const int n, T *U, const double *LowerCholSigma) icdfm(n*d, U); double tmp[n*d]; dgemm("N", "N", &dd, &nn, &dd, &one, LowerCholSigma, &dd, U, &dd, &zero, tmp, &dd); - memcpy(U, tmp, d*n*sizeof(double)); - return; + copy_n(tmp, d*n, U); } template<typename T> void -usphere(const int d, const int n, T *U) +usphere(int d, int n, T *U) { icdfm(n*d, U); #if USE_OMP @@ -167,21 +160,17 @@ usphere(const int d, const int n, T *U) int k = j*d; double norm = 0.0; for (int i = 0; i < d; i++)// dimension index. - { - norm = norm + U[k+i]*U[k+i]; - } + norm = norm + U[k+i]*U[k+i]; + norm = sqrt(norm); for (int i = 0; i < d; i++)// dimension index. - { - U[k+i] = U[k+i]/norm; - } + U[k+i] = U[k+i]/norm; } - return; } template<typename T> void -usphereRadius(const int d, const int n, double radius, T *U) +usphereRadius(int d, int n, double radius, T *U) { icdfm(n*d, U); #if USE_OMP @@ -192,14 +181,10 @@ usphereRadius(const int d, const int n, double radius, T *U) int k = j*d; double norm = 0.0; for (int i = 0; i < d; i++)// dimension index. - { - norm = norm + U[k+i]*U[k+i]; - } + norm = norm + U[k+i]*U[k+i]; + norm = sqrt(norm); for (int i = 0; i < d; i++)// dimension index. - { - U[k+i] = radius*U[k+i]/norm; - } + U[k+i] = radius*U[k+i]/norm; } - return; } diff --git a/mex/sources/sobol/qmc_sequence.cc b/mex/sources/sobol/qmc_sequence.cc index e99d805945..5b4d1e6f4d 100644 --- a/mex/sources/sobol/qmc_sequence.cc +++ b/mex/sources/sobol/qmc_sequence.cc @@ -19,17 +19,13 @@ ** along with Dynare. If not, see <http://www.gnu.org/licenses/>. **/ -#include <sstream> -#include <string.h> -#include <stdint.h> +#include <string> + #include <dynmex.h> #include "sobol.hh" #include "gaussian.hh" -// the maximum dimension defined in sobol.ff (but undef at the end) -#define DIM_MAX 1111 - void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { @@ -38,16 +34,16 @@ mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) ** prhs[0] [integer] scalar, dimension. ** prhs[1] [integer] scalar, seed. ** prhs[2] [integer] scalar, sequence type: - ** 0 ==> uniform, - ** 1 ==> gaussian, - ** 2 ==> uniform on an hypershere. + ** 0 ⇒ uniform, + ** 1 ⇒ gaussian, + ** 2 ⇒ uniform on an hypershere. ** prhs[3] [integer] scalar, sequence size. - ** prhs[4] [double] dimension*2 array, lower and upper bounds of the hypercube (default is 0-1 in all dimensions) if prhs[2]==0, - ** dimension*dimension array, covariance of the multivariate gaussian distribution of prhs[2]==1 (default is the identity matrix), + ** prhs[4] [double] dimension×2 array, lower and upper bounds of the hypercube (default is 0-1 in all dimensions) if prhs[2]==0, + ** dimension×dimension array, covariance of the multivariate gaussian distribution of prhs[2]==1 (default is the identity matrix), ** scalar, radius of the hypershere if prhs[2]==2 (default is one). ** ** OUTPUTS: - ** plhs[0] [double] sequence_size*dimension array, the Sobol sequence. + ** plhs[0] [double] sequence_size×dimension array, the Sobol sequence. ** plhs[1] [integer] scalar, seed. ** plhs[2] [integer] zero in case of success, one in case of error ** @@ -55,136 +51,104 @@ mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) /* ** Check the number of input and output arguments. */ - if (!((nrhs == 5) | (nrhs == 4) | (nrhs == 3))) - { - DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: Five, four or three input arguments are required!"); - } + if (nrhs < 3 || nrhs > 5) + DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: Five, four or three input arguments are required!"); + if (nlhs == 0) - { - DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: At least one output argument is required!"); - } + DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: At least one output argument is required!"); + /* ** Test the first input argument and assign it to dimension. */ - if (!(mxIsNumeric(prhs[0]))) - { - DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: First input (dimension) has to be a positive integer!"); - } - int dimension = (int) mxGetScalar(prhs[0]); + if (!mxIsNumeric(prhs[0])) + DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: First input (dimension) has to be a positive integer!"); + + int dimension = static_cast<int>(mxGetScalar(prhs[0])); /* ** Test the second input argument and assign it to seed. */ if (!(mxIsNumeric(prhs[1]) && mxIsClass(prhs[1], "int64"))) - { - DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: Second input (seed) has to be an integer [int64]!"); - } - int64_T seed = (int64_T) mxGetScalar(prhs[1]); + DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: Second input (seed) has to be an integer [int64]!"); + + int64_T seed = static_cast<int64_T>(mxGetScalar(prhs[1])); /* ** Test the third input argument and assign it to type (kind of QMC sequence). */ int error_flag_3 = 0; - if (!(mxIsNumeric(prhs[2]))) - { - error_flag_3 = 1; - } - int type = (int) mxGetScalar(prhs[2]); + if (!mxIsNumeric(prhs[2])) + error_flag_3 = 1; + + int type = static_cast<int>(mxGetScalar(prhs[2])); if (!(type == 0 || type == 1 || type == 2)) - { - error_flag_3 = 1; - } + error_flag_3 = 1; + if (error_flag_3 == 1) - { - DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: Third input (type of QMC sequence) has to be an integer equal to 0, 1 or 2!"); - } + DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: Third input (type of QMC sequence) has to be an integer equal to 0, 1 or 2!"); + /* - ** Test dimension>=2 when type==2 + ** Test dimension ≥ 2 when type==2 */ - if ((type == 2) && (dimension < 2)) - { - DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: First input (dimension) has to be greater than 1 for a uniform QMC on an hypershere!"); - } + if (type == 2 && dimension < 2) + DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: First input (dimension) has to be greater than 1 for a uniform QMC on an hypershere!"); + else if (dimension > DIM_MAX) - { - stringstream msg; - msg << "qmc_sequence:: First input (dimension) has to be smaller than " << DIM_MAX << " !"; - DYN_MEX_FUNC_ERR_MSG_TXT(msg.str().c_str()); - } + DYN_MEX_FUNC_ERR_MSG_TXT(("qmc_sequence:: First input (dimension) has to be smaller than " + to_string(DIM_MAX) + " !").c_str()); /* ** Test the optional fourth input argument and assign it to sequence_size. */ - if ((nrhs > 3) && !mxIsNumeric(prhs[3])) - { - DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: Fourth input (qmc sequence size) has to be a positive integer!"); - } + if (nrhs > 3 && !mxIsNumeric(prhs[3])) + DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: Fourth input (qmc sequence size) has to be a positive integer!"); + int sequence_size; if (nrhs > 3) - { - sequence_size = (int) mxGetScalar(prhs[3]); - } + sequence_size = static_cast<int>(mxGetScalar(prhs[3])); else - { - sequence_size = 1; - } + sequence_size = 1; + /* ** Test the optional fifth input argument and assign it to lower_and_upper_bounds. */ - if ((nrhs > 4) && (type == 0) && (!(mxGetN(prhs[4]) == 2)))// Sequence of uniformly distributed numbers in an hypercube. - { - DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: The fifth input argument must be an array with two columns!"); - } - if ((nrhs > 4) && (type == 0) && (!((int) mxGetM(prhs[4]) == dimension))) - { - DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: The fourth input argument must be an array with a number of lines equal to dimension (first input argument)!"); - } - if ((nrhs > 4) && (type == 1) && (!(((int) mxGetN(prhs[4]) == dimension) && ((int) mxGetM(prhs[4]) == dimension))))// Sequence of normally distributed numbers. - { - DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: The fifth input argument must be a squared matrix (whose dimension is given by the first input argument)!"); - } - if ((nrhs > 4) && (type == 2) && (!((mxGetN(prhs[4]) == 1) && (mxGetM(prhs[4]) == 1))))// Sequence of uniformly distributed numbers on an hypershere. - { - DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: The fifth input argument must be a positive scalar!"); - } - double *lower_bounds = NULL, *upper_bounds = NULL; + if (nrhs > 4 && type == 0 && mxGetN(prhs[4]) != 2) // Sequence of uniformly distributed numbers in an hypercube + DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: The fifth input argument must be an array with two columns!"); + + if (nrhs > 4 && type == 0 && static_cast<int>(mxGetM(prhs[4])) != dimension) + DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: The fourth input argument must be an array with a number of lines equal to dimension (first input argument)!"); + + if (nrhs > 4 && type == 1 && !(static_cast<int>(mxGetN(prhs[4])) == dimension + && static_cast<int>(mxGetM(prhs[4])) == dimension)) // Sequence of normally distributed numbers + DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: The fifth input argument must be a squared matrix (whose dimension is given by the first input argument)!"); + + if (nrhs > 4 && type == 2 && !(mxGetN(prhs[4]) == 1 && mxGetM(prhs[4]) == 1)) // Sequence of uniformly distributed numbers on a hypershere + DYN_MEX_FUNC_ERR_MSG_TXT("qmc_sequence:: The fifth input argument must be a positive scalar!"); + + const double *lower_bounds = nullptr, *upper_bounds = nullptr; int unit_hypercube_flag = 1; - if ((type == 0) && (nrhs > 4)) + if (type == 0 && nrhs > 4) { - lower_bounds = (double *) mxCalloc(dimension, sizeof(double)); - upper_bounds = (double *) mxCalloc(dimension, sizeof(double)); - double *tmp; - tmp = (double *) mxCalloc(dimension*2, sizeof(double)); - memcpy(tmp, mxGetPr(prhs[4]), dimension*2*sizeof(double)); - lower_bounds = &tmp[0]; - upper_bounds = &tmp[dimension]; + lower_bounds = mxGetPr(prhs[4]); + upper_bounds = lower_bounds + dimension; unit_hypercube_flag = 0; } - double *cholcov; + const double *cholcov = nullptr; int identity_covariance_matrix = 1; - if ((type == 1) && (nrhs > 4)) + if (type == 1 && nrhs > 4) { - cholcov = (double *) mxCalloc(dimension*dimension, sizeof(double)); - double *tmp; - tmp = (double *) mxCalloc(dimension*dimension, sizeof(double)); - memcpy(tmp, mxGetPr(prhs[4]), dimension*dimension*sizeof(double)); - cholcov = &tmp[0]; + cholcov = mxGetPr(prhs[4]); identity_covariance_matrix = 0; } double radius = 1.0; int unit_radius = 1; - if ((type == 2) && (nrhs > 4)) + if (type == 2 && nrhs > 4) { - double *tmp; - tmp = (double *) mxCalloc(1, sizeof(double)); - memcpy(tmp, mxGetPr(prhs[4]), sizeof(double)); - radius = tmp[0]; + radius = *mxGetPr(prhs[4]); unit_radius = 0; } /* ** Initialize outputs of the mex file. */ - double *qmc_draws; plhs[0] = mxCreateDoubleMatrix(dimension, sequence_size, mxREAL); - qmc_draws = mxGetPr(plhs[0]); + double *qmc_draws = mxGetPr(plhs[0]); int64_T seed_out; if (sequence_size == 1) @@ -195,16 +159,16 @@ mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) else seed_out = sobol_block(dimension, sequence_size, seed, qmc_draws); - if (type == 0 && unit_hypercube_flag == 0) // Uniform QMC sequence in an hypercube. + if (type == 0 && unit_hypercube_flag == 0) // Uniform QMC sequence in an hypercube expand_unit_hypercube(dimension, sequence_size, qmc_draws, lower_bounds, upper_bounds); - else if (type == 1)// Normal QMC sequance in R^n. + else if (type == 1) // Normal QMC sequence in ℝⁿ { if (identity_covariance_matrix == 1) icdfm(dimension*sequence_size, qmc_draws); else icdfmSigma(dimension, sequence_size, qmc_draws, cholcov); } - else if (type == 2)// Uniform QMC sequence on an hypershere. + else if (type == 2) // Uniform QMC sequence on a hypershere { if (unit_radius == 1) usphere(dimension, sequence_size, qmc_draws); @@ -215,7 +179,7 @@ mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) if (nlhs >= 2) { plhs[1] = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); - *((int64_T *) mxGetData(plhs[1])) = seed_out; + *(reinterpret_cast<int64_T *>(mxGetData(plhs[1]))) = seed_out; } if (nlhs >= 3) plhs[2] = mxCreateDoubleScalar(0); diff --git a/mex/sources/sobol/sobol.hh b/mex/sources/sobol/sobol.hh index 8926daca92..b39795314c 100644 --- a/mex/sources/sobol/sobol.hh +++ b/mex/sources/sobol/sobol.hh @@ -29,7 +29,7 @@ using namespace std; -#define DIM_MAX 1111 +constexpr int DIM_MAX = 1111; template<typename T> int @@ -130,9 +130,8 @@ bit_lo0(T n) bit++; T n2 = n/2; if (n == 2*n2) - { - break; - } + break; + n = n2; } return bit; @@ -157,8 +156,7 @@ ixor(T i, T j) { T i2 = i / 2; T j2 = j / 2; - if ( - ((i == 2 * i2) && (j != 2 * j2)) + if (((i == 2 * i2) && (j != 2 * j2)) || ((i != 2 * i2) && (j == 2 * j2))) { k = k + l; @@ -392,7 +390,7 @@ next_sobol(int dim_num, T1 *seed, T2 *quasi) /* ** Set ATMOST = 2^LOG_MAX - 1. */ - atmost = (T1) 0; + atmost = static_cast<T1>(0); for (int i = 1; i <= LOG_MAX; i++) atmost = 2 * atmost + 1; /* @@ -404,7 +402,7 @@ next_sobol(int dim_num, T1 *seed, T2 *quasi) */ for (T1 j = 0; j < maxcol; j++) { - v[0][j] = (T1) 1; + v[0][j] = static_cast<T1>(1); } /* ** Initialize the remaining rows of V. @@ -423,9 +421,8 @@ next_sobol(int dim_num, T1 *seed, T2 *quasi) { j = j / 2; if (j <= 0) - { - break; - } + break; + m = m + 1; } /* @@ -453,9 +450,7 @@ next_sobol(int dim_num, T1 *seed, T2 *quasi) { l = 2 * l; if (includ[k]) - { - newv = (newv ^ (l * v[i][j-k-1])); - } + newv = newv ^ (l * v[i][j-k-1]); } v[i][j] = newv; } @@ -468,14 +463,12 @@ next_sobol(int dim_num, T1 *seed, T2 *quasi) { l = 2 * l; for (int i = 0; i < dim_num; i++) - { - v[i][j] = v[i][j] * l; - } + v[i][j] = v[i][j] * l; } /* ** RECIPD is 1/(common denominator of the elements in V). */ - recipd = 1.0E+00 / ((T2) (2 * l)); + recipd = 1.0E+00 / static_cast<T2>(2 * l); } if (*seed < 0) *seed = 0; @@ -484,14 +477,10 @@ next_sobol(int dim_num, T1 *seed, T2 *quasi) { l = 1; for (int i = 0; i < dim_num; i++) - { - lastq[i] = 0; - } + lastq[i] = 0; } else if (*seed == seed_save + 1) - { - l = bit_lo0(*seed); - } + l = bit_lo0(*seed); else if (*seed <= seed_save) { seed_save = 0; @@ -502,9 +491,7 @@ next_sobol(int dim_num, T1 *seed, T2 *quasi) { l = bit_lo0(seed_temp); for (int i = 0; i < dim_num; i++) - { - lastq[i] = (lastq[i] ^ v[i][l-1]); - } + lastq[i] = (lastq[i] ^ v[i][l-1]); } l = bit_lo0(*seed); } @@ -514,9 +501,7 @@ next_sobol(int dim_num, T1 *seed, T2 *quasi) { l = bit_lo0(seed_temp); for (int i = 0; i < dim_num; i++) - { - lastq[i] = (lastq[i] ^ v[i][l-1]); - } + lastq[i] = (lastq[i] ^ v[i][l-1]); } l = bit_lo0(*seed); } @@ -539,8 +524,8 @@ next_sobol(int dim_num, T1 *seed, T2 *quasi) */ for (int i = 0; i < dim_num; i++) { - quasi[i] = ((T2) lastq[i]) * recipd; - lastq[i] = (lastq[i]^v[i][l-1]); + quasi[i] = static_cast<T2>(lastq[i]) * recipd; + lastq[i] = lastq[i]^v[i][l-1]; } seed_save = *seed; *seed = *seed + 1; @@ -552,31 +537,24 @@ T1 sobol_block(int dimension, int block_size, T1 seed, T2 *block) { for (int iter = 0; iter < block_size; iter++) - { - next_sobol(dimension, &seed, &block[iter*dimension]); - } + next_sobol(dimension, &seed, &block[iter*dimension]); return seed; } template<typename T> void -expand_unit_hypercube(int dimension, int block_size, T *block, T *lower_bound, T *upper_bound) +expand_unit_hypercube(int dimension, int block_size, T *block, const T *lower_bound, const T *upper_bound) { T *hypercube_length = new T[dimension]; for (int dim = 0; dim < dimension; dim++) - { - hypercube_length[dim] = upper_bound[dim]-lower_bound[dim]; - } + hypercube_length[dim] = upper_bound[dim]-lower_bound[dim]; + int base = 0; for (int sim = 0; sim < block_size; sim++) { for (int dim = 0; dim < dimension; dim++) - { - block[base+dim] = lower_bound[dim] + hypercube_length[dim]*block[base+dim]; - } + block[base+dim] = lower_bound[dim] + hypercube_length[dim]*block[base+dim]; base += dimension; } delete[] hypercube_length; } - -#undef DIM_MAX -- GitLab