2011-12-21 18:23:15 +01:00
/*
2011-12-22 11:32:53 +01:00
* * Computes Quasi Monte - Carlo sequence .
* *
2022-03-18 18:18:24 +01:00
* * Copyright © 2010 - 2022 Dynare Team
2011-12-22 11:32:53 +01:00
* *
* * This file is part of Dynare ( can be used outside Dynare ) .
* *
* * Dynare is free software : you can redistribute it and / or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation , either version 3 of the License , or
* * ( at your option ) any later version .
* *
* * Dynare is distributed in the hope that it will be useful ,
* * but WITHOUT ANY WARRANTY ; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* * GNU General Public License for more details .
* *
* * You should have received a copy of the GNU General Public License
2021-06-09 17:33:48 +02:00
* * along with Dynare . If not , see < https : //www.gnu.org/licenses/>.
2011-12-22 11:32:53 +01:00
* */
2011-12-21 18:23:15 +01:00
2019-04-03 18:49:32 +02:00
# include <string>
2011-12-21 18:23:15 +01:00
# include <dynmex.h>
# include "sobol.hh"
# include "gaussian.hh"
2017-05-16 16:30:27 +02:00
void
mexFunction ( int nlhs , mxArray * plhs [ ] , int nrhs , const mxArray * prhs [ ] )
2011-12-21 18:23:15 +01:00
{
2017-05-16 16:30:27 +02:00
/*
2011-12-21 18:23:15 +01:00
* * INPUTS :
* * prhs [ 0 ] [ integer ] scalar , dimension .
* * prhs [ 1 ] [ integer ] scalar , seed .
* * prhs [ 2 ] [ integer ] scalar , sequence type :
2019-04-03 18:49:32 +02:00
* * 0 ⇒ uniform ,
* * 1 ⇒ gaussian ,
* * 2 ⇒ uniform on an hypershere .
2011-12-21 18:23:15 +01:00
* * prhs [ 3 ] [ integer ] scalar , sequence size .
2019-04-03 18:49:32 +02:00
* * 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 ) ,
2011-12-21 18:23:15 +01:00
* * scalar , radius of the hypershere if prhs [ 2 ] = = 2 ( default is one ) .
* *
* * OUTPUTS :
2019-04-03 18:49:32 +02:00
* * plhs [ 0 ] [ double ] sequence_size × dimension array , the Sobol sequence .
2011-12-26 16:09:33 +01:00
* * plhs [ 1 ] [ integer ] scalar , seed .
* * plhs [ 2 ] [ integer ] zero in case of success , one in case of error
2017-05-16 16:30:27 +02:00
* *
2011-12-21 18:23:15 +01:00
*/
/*
* * Check the number of input and output arguments .
*/
2019-04-03 18:49:32 +02:00
if ( nrhs < 3 | | nrhs > 5 )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " qmc_sequence:: Five, four or three input arguments are required! " ) ;
2019-04-03 18:49:32 +02:00
2011-12-26 16:09:33 +01:00
if ( nlhs = = 0 )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " qmc_sequence:: At least one output argument is required! " ) ;
2019-04-03 18:49:32 +02:00
2011-12-21 18:23:15 +01:00
/*
* * Test the first input argument and assign it to dimension .
*/
2022-03-18 18:18:24 +01:00
if ( ! ( mxIsScalar ( prhs [ 0 ] ) & & mxIsNumeric ( prhs [ 0 ] ) ) )
mexErrMsgTxt ( " qmc_sequence:: First input (dimension) has to be a numeric scalar! " ) ;
2019-04-03 18:49:32 +02:00
int dimension = static_cast < int > ( mxGetScalar ( prhs [ 0 ] ) ) ;
2022-03-18 18:18:24 +01:00
if ( dimension < = 0 )
mexErrMsgTxt ( " qmc_sequence:: First input (dimension) has to be a positive integer! " ) ;
2011-12-21 18:23:15 +01:00
/*
* * Test the second input argument and assign it to seed .
*/
2017-05-16 16:30:27 +02:00
if ( ! ( mxIsNumeric ( prhs [ 1 ] ) & & mxIsClass ( prhs [ 1 ] , " int64 " ) ) )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " qmc_sequence:: Second input (seed) has to be an integer [int64]! " ) ;
2019-04-03 18:49:32 +02:00
int64_T seed = static_cast < int64_T > ( mxGetScalar ( prhs [ 1 ] ) ) ;
2011-12-21 18:23:15 +01:00
/*
* * Test the third input argument and assign it to type ( kind of QMC sequence ) .
*/
int error_flag_3 = 0 ;
2022-03-18 18:18:24 +01:00
if ( ! ( mxIsScalar ( prhs [ 2 ] ) & & mxIsNumeric ( prhs [ 2 ] ) ) )
2019-04-03 18:49:32 +02:00
error_flag_3 = 1 ;
int type = static_cast < int > ( mxGetScalar ( prhs [ 2 ] ) ) ;
2017-05-16 16:30:27 +02:00
if ( ! ( type = = 0 | | type = = 1 | | type = = 2 ) )
2019-04-03 18:49:32 +02:00
error_flag_3 = 1 ;
2017-05-16 16:30:27 +02:00
if ( error_flag_3 = = 1 )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " qmc_sequence:: Third input (type of QMC sequence) has to be an integer equal to 0, 1 or 2! " ) ;
2019-04-03 18:49:32 +02:00
2011-12-21 18:23:15 +01:00
/*
2019-04-03 18:49:32 +02:00
* * Test dimension ≥ 2 when type = = 2
2011-12-21 18:23:15 +01:00
*/
2019-04-03 18:49:32 +02:00
if ( type = = 2 & & dimension < 2 )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( " qmc_sequence:: First input (dimension) has to be greater than 1 for a uniform QMC on an hypershere! " ) ;
2019-04-03 18:49:32 +02:00
2014-02-15 21:02:33 +01:00
else if ( dimension > DIM_MAX )
2020-01-10 17:55:57 +01:00
mexErrMsgTxt ( ( " qmc_sequence:: First input (dimension) has to be smaller than " + to_string ( DIM_MAX ) + " ! " ) . c_str ( ) ) ;
2014-02-15 21:02:33 +01:00
2011-12-21 18:23:15 +01:00
/*
* * Test the optional fourth input argument and assign it to sequence_size .
*/
2022-03-18 18:18:24 +01:00
if ( nrhs > 3 & & ! ( mxIsScalar ( prhs [ 3 ] ) & & mxIsNumeric ( prhs [ 3 ] ) ) )
mexErrMsgTxt ( " qmc_sequence:: Fourth input (qmc sequence size) has to be a numeric scalar! " ) ;
2019-04-03 18:49:32 +02:00
2011-12-21 18:23:15 +01:00
int sequence_size ;
2017-05-16 16:30:27 +02:00
if ( nrhs > 3 )
2022-03-18 18:18:24 +01:00
{
sequence_size = static_cast < int > ( mxGetScalar ( prhs [ 3 ] ) ) ;
if ( sequence_size < = 0 )
mexErrMsgTxt ( " qmc_sequence:: Fourth input (qmc sequence size) has to be a positive integer! " ) ;
}
2011-12-21 18:23:15 +01:00
else
2019-04-03 18:49:32 +02:00
sequence_size = 1 ;
2011-12-21 18:23:15 +01:00
/*
* * Test the optional fifth input argument and assign it to lower_and_upper_bounds .
*/
2022-03-18 18:18:24 +01:00
if ( nrhs > 4 & & type = = 0
& & ! ( mxIsDouble ( prhs [ 4 ] ) & & ! mxIsComplex ( prhs [ 4 ] ) & & ! mxIsSparse ( prhs [ 4 ] )
& & mxGetN ( prhs [ 4 ] ) = = 2
& & static_cast < int > ( mxGetM ( prhs [ 4 ] ) ) = = dimension ) ) // Sequence of uniformly distributed numbers in an hypercube
mexErrMsgTxt ( " qmc_sequence:: The fifth input argument must be a real dense array with two columns and number of lines equal to dimension (first input argument)! " ) ;
if ( nrhs > 4 & & type = = 1
& & ! ( mxIsDouble ( prhs [ 4 ] ) & & ! mxIsComplex ( prhs [ 4 ] ) & & ! mxIsSparse ( prhs [ 4 ] )
& & static_cast < int > ( mxGetN ( prhs [ 4 ] ) ) = = dimension
& & static_cast < int > ( mxGetM ( prhs [ 4 ] ) ) = = dimension ) ) // Sequence of normally distributed numbers
mexErrMsgTxt ( " qmc_sequence:: The fifth input argument must be a real dense square matrix (whose dimension is given by the first input argument)! " ) ;
if ( nrhs > 4 & & type = = 2
& & ! ( mxIsScalar ( prhs [ 4 ] ) & & mxIsNumeric ( prhs [ 4 ] ) ) ) // Sequence of uniformly distributed numbers on a hypershere
mexErrMsgTxt ( " qmc_sequence:: The fifth input argument must be a numeric scalar! " ) ;
2019-04-03 18:49:32 +02:00
const double * lower_bounds = nullptr , * upper_bounds = nullptr ;
2011-12-21 18:23:15 +01:00
int unit_hypercube_flag = 1 ;
2019-04-03 18:49:32 +02:00
if ( type = = 0 & & nrhs > 4 )
2011-12-21 18:23:15 +01:00
{
2019-04-03 18:49:32 +02:00
lower_bounds = mxGetPr ( prhs [ 4 ] ) ;
upper_bounds = lower_bounds + dimension ;
2011-12-21 18:23:15 +01:00
unit_hypercube_flag = 0 ;
}
2019-04-03 18:49:32 +02:00
const double * cholcov = nullptr ;
2011-12-21 18:23:15 +01:00
int identity_covariance_matrix = 1 ;
2019-04-03 18:49:32 +02:00
if ( type = = 1 & & nrhs > 4 )
2011-12-21 18:23:15 +01:00
{
2019-04-03 18:49:32 +02:00
cholcov = mxGetPr ( prhs [ 4 ] ) ;
2011-12-21 18:23:15 +01:00
identity_covariance_matrix = 0 ;
}
double radius = 1.0 ;
int unit_radius = 1 ;
2019-04-03 18:49:32 +02:00
if ( type = = 2 & & nrhs > 4 )
2011-12-21 18:23:15 +01:00
{
2022-03-18 18:18:24 +01:00
radius = mxGetScalar ( prhs [ 4 ] ) ;
if ( radius < = 0 )
mexErrMsgTxt ( " qmc_sequence:: The fifth input argument must be a positive real number! " ) ;
2011-12-21 18:23:15 +01:00
unit_radius = 0 ;
}
/*
* * Initialize outputs of the mex file .
*/
2017-05-16 16:30:27 +02:00
plhs [ 0 ] = mxCreateDoubleMatrix ( dimension , sequence_size , mxREAL ) ;
2019-04-03 18:49:32 +02:00
double * qmc_draws = mxGetPr ( plhs [ 0 ] ) ;
2011-12-26 16:09:33 +01:00
int64_T seed_out ;
2017-05-16 16:30:27 +02:00
if ( sequence_size = = 1 )
2011-12-26 16:09:33 +01:00
{
2017-05-16 16:30:27 +02:00
next_sobol ( dimension , & seed , qmc_draws ) ;
2011-12-26 16:09:33 +01:00
seed_out = seed ;
}
else
2017-05-16 16:30:27 +02:00
seed_out = sobol_block ( dimension , sequence_size , seed , qmc_draws ) ;
2011-12-26 16:09:33 +01:00
2019-04-03 18:49:32 +02:00
if ( type = = 0 & & unit_hypercube_flag = = 0 ) // Uniform QMC sequence in an hypercube
2017-05-16 16:30:27 +02:00
expand_unit_hypercube ( dimension , sequence_size , qmc_draws , lower_bounds , upper_bounds ) ;
2019-04-03 18:49:32 +02:00
else if ( type = = 1 ) // Normal QMC sequence in ℝⁿ
2011-12-26 16:09:33 +01:00
{
2017-05-16 16:30:27 +02:00
if ( identity_covariance_matrix = = 1 )
2011-12-26 16:09:33 +01:00
icdfm ( dimension * sequence_size , qmc_draws ) ;
2011-12-21 18:23:15 +01:00
else
2017-05-16 16:30:27 +02:00
icdfmSigma ( dimension , sequence_size , qmc_draws , cholcov ) ;
2011-12-26 16:09:33 +01:00
}
2019-04-03 18:49:32 +02:00
else if ( type = = 2 ) // Uniform QMC sequence on a hypershere
2011-12-26 16:09:33 +01:00
{
2017-05-16 16:30:27 +02:00
if ( unit_radius = = 1 )
2011-12-26 16:09:33 +01:00
usphere ( dimension , sequence_size , qmc_draws ) ;
2011-12-21 18:23:15 +01:00
else
2011-12-26 16:09:33 +01:00
usphereRadius ( dimension , sequence_size , radius , qmc_draws ) ;
}
2017-05-16 16:30:27 +02:00
2011-12-26 16:09:33 +01:00
if ( nlhs > = 2 )
{
plhs [ 1 ] = mxCreateNumericMatrix ( 1 , 1 , mxINT64_CLASS , mxREAL ) ;
2019-07-09 11:51:23 +02:00
# if MX_HAS_INTERLEAVED_COMPLEX
* mxGetInt64s ( plhs [ 1 ] ) = seed_out ;
# else
2019-04-23 12:58:38 +02:00
* ( static_cast < int64_T * > ( mxGetData ( plhs [ 1 ] ) ) ) = seed_out ;
2019-07-09 11:51:23 +02:00
# endif
2011-12-21 18:23:15 +01:00
}
2011-12-26 16:09:33 +01:00
if ( nlhs > = 3 )
plhs [ 2 ] = mxCreateDoubleScalar ( 0 ) ;
2011-12-21 18:23:15 +01:00
}