2009-05-06 12:10:27 +02:00
/*
2019-04-16 11:40:38 +02:00
* Copyright © 2008 - 2019 Dynare Team
2009-05-19 10:57:07 +02:00
*
* This file is part of 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
* along with Dynare . If not , see < http : //www.gnu.org/licenses/>.
*/
2009-05-06 12:10:27 +02:00
2019-04-02 16:33:15 +02:00
/* Defines the entry point for the k-order perturbation application DLL.
2010-08-30 17:11:58 +02:00
2019-04-02 16:33:15 +02:00
See matlab / mex / k_order_perturbation . m for a description of inputs and
outputs .
2010-08-30 17:11:58 +02:00
*/
2009-05-06 12:10:27 +02:00
2010-12-17 18:34:23 +01:00
# include "dynamic_m.hh"
2009-12-17 12:00:50 +01:00
# include "dynamic_dll.hh"
2019-04-02 16:33:15 +02:00
# include "k_ord_dynare.hh"
# include "approximation.hh"
# include "exception.hh"
# include "dynare_exception.hh"
# include "kord_exception.hh"
# include "tl_exception.hh"
# include "SylvException.hh"
2009-05-06 12:10:27 +02:00
2019-03-07 18:17:43 +01:00
# include <algorithm>
2010-02-08 16:58:24 +01:00
# include <cassert>
2009-05-06 12:10:27 +02:00
2019-03-07 18:17:43 +01:00
# include "dynmex.h"
2010-09-20 16:57:02 +02:00
2019-04-08 16:59:48 +02:00
/* Vector for storing field names like “g_0”, “g_1”, …
A static structure is needed since MATLAB apparently does not create its own
copy of the strings ( contrary to what is said at :
https : //fr.mathworks.com/matlabcentral/answers/315937-mxcreatestructarray-and-mxcreatestructmatrix-field-name-memory-management
) */
std : : vector < std : : string > g_fieldnames ;
2019-04-08 17:53:20 +02:00
/* Convert MATLAB Dynare endo and exo names cell array to a vector<string> array of
string pointers . */
std : : vector < std : : string >
DynareMxArrayToString ( const mxArray * mxFldp )
2009-11-30 17:31:27 +01:00
{
2019-04-08 17:53:20 +02:00
assert ( mxIsCell ( mxFldp ) ) ;
std : : vector < std : : string > r ;
for ( size_t i = 0 ; i < mxGetNumberOfElements ( mxFldp ) ; i + + )
r . emplace_back ( mxArrayToString ( mxGetCell ( mxFldp , i ) ) ) ;
2010-03-09 18:22:33 +01:00
2019-04-08 17:53:20 +02:00
return r ;
2009-11-30 17:31:27 +01:00
}
2017-05-16 16:30:27 +02:00
void
2019-04-08 16:59:48 +02:00
copy_derivatives ( mxArray * destin , const Symmetry & sym , const FGSContainer & derivs , const char * fieldname )
2012-07-31 21:50:28 +02:00
{
2019-04-02 16:39:32 +02:00
const FGSTensor & x = derivs . get ( sym ) ;
auto x_unfolded = x . unfold ( ) ;
2019-04-16 12:40:50 +02:00
int n = x_unfolded - > nrows ( ) ;
int m = x_unfolded - > ncols ( ) ;
2012-07-31 21:50:28 +02:00
mxArray * tmp = mxCreateDoubleMatrix ( n , m , mxREAL ) ;
2019-04-02 16:39:32 +02:00
std : : copy_n ( x_unfolded - > getData ( ) . base ( ) , n * m , mxGetPr ( tmp ) ) ;
2019-04-08 16:59:48 +02:00
mxSetField ( destin , 0 , fieldname , tmp ) ;
2012-07-31 21:50:28 +02:00
}
2009-05-06 12:10:27 +02:00
extern " C " {
2009-05-27 16:28:23 +02:00
void
mexFunction ( int nlhs , mxArray * plhs [ ] ,
int nrhs , const mxArray * prhs [ ] )
2009-05-06 12:10:27 +02:00
{
2012-07-07 21:21:28 +02:00
if ( nrhs < 3 | | nlhs < 2 )
DYN_MEX_FUNC_ERR_MSG_TXT ( " Must have at least 3 input parameters and takes at least 2 output parameters. " ) ;
2009-12-16 18:18:38 +01:00
2009-05-27 16:28:23 +02:00
const mxArray * dr = prhs [ 0 ] ;
2010-08-30 17:11:58 +02:00
const mxArray * M_ = prhs [ 1 ] ;
const mxArray * options_ = prhs [ 2 ] ;
2019-03-07 18:17:43 +01:00
bool use_dll = mxGetScalar ( mxGetField ( options_ , 0 , " use_dll " ) ) ! = 0 ;
2009-05-27 16:28:23 +02:00
mxArray * mFname = mxGetField ( M_ , 0 , " fname " ) ;
if ( ! mxIsChar ( mFname ) )
2010-09-20 16:57:02 +02:00
DYN_MEX_FUNC_ERR_MSG_TXT ( " Input must be of type char. " ) ;
2019-02-06 15:50:01 +01:00
std : : string fName = mxArrayToString ( mFname ) ;
2009-05-27 16:28:23 +02:00
2009-05-06 12:10:27 +02:00
int kOrder ;
2009-05-27 16:28:23 +02:00
mxArray * mxFldp = mxGetField ( options_ , 0 , " order " ) ;
2019-04-10 09:41:45 +02:00
if ( ! mxIsNumeric ( mxFldp ) )
DYN_MEX_FUNC_ERR_MSG_TXT ( " options_.order must be a numeric value " ) ;
kOrder = static_cast < int > ( mxGetScalar ( mxFldp ) ) ;
2019-04-15 17:24:05 +02:00
if ( kOrder < 1 )
DYN_MEX_FUNC_ERR_MSG_TXT ( " options_.order must be at least 1 " ) ;
2009-12-16 18:18:38 +01:00
2009-05-06 12:10:27 +02:00
double qz_criterium = 1 + 1e-6 ;
2009-05-27 16:28:23 +02:00
mxFldp = mxGetField ( options_ , 0 , " qz_criterium " ) ;
2014-01-27 15:33:37 +01:00
if ( mxGetNumberOfElements ( mxFldp ) > 0 & & mxIsNumeric ( mxFldp ) )
2019-03-07 18:17:43 +01:00
qz_criterium = mxGetScalar ( mxFldp ) ;
2009-05-27 16:28:23 +02:00
2010-08-30 17:11:58 +02:00
mxFldp = mxGetField ( M_ , 0 , " params " ) ;
Dynare++ / sylvester equation solver: refactor Vector and ConstVector classes
- these classes now encapsulate a std::shared_ptr<{const, }double>, so that
they do not perform memory management, and several {Const,}Vector instances
can transparently share the same underlying data
- make converting constructor from ConstVector to Vector explicit, since that
entails memory allocation (but the reverse conversion is almost costless, so
keep it implicit); do the same for GeneralMatrix/ConstGeneralMatrix,
TwoDMatrix/ConstTwoDMatrix
- remove the constructors that were extracting a row/column from a matrix, and
replace them by getRow() and getCol() methods on {Const,}GeneralMatrix
- rename and change the API of the complex version Vector::add(), so that it is
explicit that it deals with complex numbers
- add constructors that take a MATLAB mxArray
2019-01-22 16:07:44 +01:00
Vector modParams { mxFldp } ;
2011-02-07 11:12:32 +01:00
if ( ! modParams . isFinite ( ) )
DYN_MEX_FUNC_ERR_MSG_TXT ( " The parameters vector contains NaN or Inf " ) ;
2009-05-27 16:28:23 +02:00
2010-08-30 17:11:58 +02:00
mxFldp = mxGetField ( M_ , 0 , " Sigma_e " ) ;
2019-04-10 09:41:45 +02:00
int dim = static_cast < int > ( mxGetN ( mxFldp ) ) ;
TwoDMatrix vCov ( dim , dim , Vector { mxFldp } ) ;
2011-02-07 11:12:32 +01:00
if ( ! vCov . isFinite ( ) )
DYN_MEX_FUNC_ERR_MSG_TXT ( " The covariance matrix of shocks contains NaN or Inf " ) ;
2009-05-27 16:28:23 +02:00
2010-08-30 17:11:58 +02:00
mxFldp = mxGetField ( dr , 0 , " ys " ) ; // and not in order of dr.order_var
Dynare++ / sylvester equation solver: refactor Vector and ConstVector classes
- these classes now encapsulate a std::shared_ptr<{const, }double>, so that
they do not perform memory management, and several {Const,}Vector instances
can transparently share the same underlying data
- make converting constructor from ConstVector to Vector explicit, since that
entails memory allocation (but the reverse conversion is almost costless, so
keep it implicit); do the same for GeneralMatrix/ConstGeneralMatrix,
TwoDMatrix/ConstTwoDMatrix
- remove the constructors that were extracting a row/column from a matrix, and
replace them by getRow() and getCol() methods on {Const,}GeneralMatrix
- rename and change the API of the complex version Vector::add(), so that it is
explicit that it deals with complex numbers
- add constructors that take a MATLAB mxArray
2019-01-22 16:07:44 +01:00
Vector ySteady { mxFldp } ;
2011-02-07 11:12:32 +01:00
if ( ! ySteady . isFinite ( ) )
DYN_MEX_FUNC_ERR_MSG_TXT ( " The steady state vector contains NaN or Inf " ) ;
2009-05-27 16:28:23 +02:00
2012-11-16 20:05:13 +01:00
mxFldp = mxGetField ( M_ , 0 , " nstatic " ) ;
2019-03-07 18:17:43 +01:00
const int nStat = static_cast < int > ( mxGetScalar ( mxFldp ) ) ;
2012-11-16 20:05:13 +01:00
mxFldp = mxGetField ( M_ , 0 , " npred " ) ;
2019-03-07 18:17:43 +01:00
const int nPred = static_cast < int > ( mxGetScalar ( mxFldp ) ) ;
2012-11-16 20:05:13 +01:00
mxFldp = mxGetField ( M_ , 0 , " nboth " ) ;
2019-03-07 18:17:43 +01:00
const int nBoth = static_cast < int > ( mxGetScalar ( mxFldp ) ) ;
2012-11-16 20:05:13 +01:00
mxFldp = mxGetField ( M_ , 0 , " nfwrd " ) ;
2019-03-07 18:17:43 +01:00
const int nForw = static_cast < int > ( mxGetScalar ( mxFldp ) ) ;
2009-05-27 16:28:23 +02:00
mxFldp = mxGetField ( M_ , 0 , " exo_nbr " ) ;
2019-03-07 18:17:43 +01:00
const int nExog = static_cast < int > ( mxGetScalar ( mxFldp ) ) ;
2009-05-27 16:28:23 +02:00
mxFldp = mxGetField ( M_ , 0 , " endo_nbr " ) ;
2019-03-07 18:17:43 +01:00
const int nEndo = static_cast < int > ( mxGetScalar ( mxFldp ) ) ;
2009-05-27 16:28:23 +02:00
mxFldp = mxGetField ( M_ , 0 , " param_nbr " ) ;
2019-03-07 18:17:43 +01:00
const int nPar = static_cast < int > ( mxGetScalar ( mxFldp ) ) ;
2009-08-24 18:01:25 +02:00
2010-08-30 17:11:58 +02:00
mxFldp = mxGetField ( dr , 0 , " order_var " ) ;
2019-04-10 09:41:45 +02:00
dim = static_cast < int > ( mxGetM ( mxFldp ) ) ;
if ( dim ! = nEndo )
DYN_MEX_FUNC_ERR_MSG_TXT ( " Incorrect size of dr.order_var " ) ;
2019-04-10 09:23:32 +02:00
std : : vector < int > dr_order ( nEndo ) ;
2019-04-10 09:41:45 +02:00
std : : transform ( mxGetPr ( mxFldp ) , mxGetPr ( mxFldp ) + dim , dr_order . begin ( ) ,
2019-04-10 09:23:32 +02:00
[ ] ( double x ) { return static_cast < int > ( x ) - 1 ; } ) ;
2009-05-27 16:28:23 +02:00
2009-05-06 12:10:27 +02:00
// the lag, current and lead blocks of the jacobian respectively
2019-04-10 18:03:25 +02:00
TwoDMatrix llincidence ( mxGetField ( M_ , 0 , " lead_lag_incidence " ) ) ;
if ( llincidence . nrows ( ) ! = 3 | | llincidence . ncols ( ) ! = nEndo )
2019-04-10 09:41:45 +02:00
DYN_MEX_FUNC_ERR_MSG_TXT ( " Incorrect size of M_.lead_lag_incidence " ) ;
2019-03-07 18:17:43 +01:00
2009-08-24 18:01:25 +02:00
mxFldp = mxGetField ( M_ , 0 , " NNZDerivatives " ) ;
Dynare++ / sylvester equation solver: refactor Vector and ConstVector classes
- these classes now encapsulate a std::shared_ptr<{const, }double>, so that
they do not perform memory management, and several {Const,}Vector instances
can transparently share the same underlying data
- make converting constructor from ConstVector to Vector explicit, since that
entails memory allocation (but the reverse conversion is almost costless, so
keep it implicit); do the same for GeneralMatrix/ConstGeneralMatrix,
TwoDMatrix/ConstTwoDMatrix
- remove the constructors that were extracting a row/column from a matrix, and
replace them by getRow() and getCol() methods on {Const,}GeneralMatrix
- rename and change the API of the complex version Vector::add(), so that it is
explicit that it deals with complex numbers
- add constructors that take a MATLAB mxArray
2019-01-22 16:07:44 +01:00
Vector NNZD { mxFldp } ;
2010-07-17 10:14:22 +02:00
if ( NNZD [ kOrder - 1 ] = = - 1 )
2019-04-10 09:41:45 +02:00
DYN_MEX_FUNC_ERR_MSG_TXT ( " The derivatives were not computed for the required order. Make sure that you used the right order option inside the `stoch_simul' command " ) ;
2009-05-27 16:28:23 +02:00
2019-04-10 09:23:32 +02:00
mxFldp = mxGetField ( M_ , 0 , " endo_names " ) ;
2019-04-08 17:53:20 +02:00
std : : vector < std : : string > endoNames = DynareMxArrayToString ( mxFldp ) ;
2009-05-27 16:28:23 +02:00
2010-08-30 17:11:58 +02:00
mxFldp = mxGetField ( M_ , 0 , " exo_names " ) ;
2019-04-08 17:53:20 +02:00
std : : vector < std : : string > exoNames = DynareMxArrayToString ( mxFldp ) ;
2009-05-27 16:28:23 +02:00
2019-04-08 17:53:20 +02:00
if ( nEndo ! = static_cast < int > ( endoNames . size ( ) ) | | nExog ! = static_cast < int > ( exoNames . size ( ) ) )
2019-04-10 09:41:45 +02:00
DYN_MEX_FUNC_ERR_MSG_TXT ( " Incorrect size of M_.endo_names or M_.exo_names " ) ;
2009-05-27 16:28:23 +02:00
2019-04-12 18:16:42 +02:00
mxFldp = mxGetField ( M_ , 0 , " dynamic_tmp_nbr " ) ;
if ( static_cast < int > ( mxGetM ( mxFldp ) ) < kOrder + 1 | | mxGetN ( mxFldp ) ! = 1 )
DYN_MEX_FUNC_ERR_MSG_TXT ( " Incorrect size of M_.dynamic_tmp_nbr " ) ;
int ntt = std : : accumulate ( mxGetPr ( mxFldp ) , mxGetPr ( mxFldp ) + kOrder + 1 , 0 ) ;
2009-05-27 16:28:23 +02:00
const int nSteps = 0 ; // Dynare++ solving steps, for time being default to 0 = deterministic steady state
try
{
2019-03-07 18:17:43 +01:00
Journal journal ( fName + " .jnl " ) ;
2010-12-17 18:34:23 +01:00
2019-02-06 15:50:01 +01:00
std : : unique_ptr < DynamicModelAC > dynamicModelFile ;
2019-03-07 18:17:43 +01:00
if ( use_dll )
2019-04-15 16:46:39 +02:00
dynamicModelFile = std : : make_unique < DynamicModelDLL > ( fName , ntt , kOrder ) ;
2010-12-17 18:34:23 +01:00
else
2019-04-12 18:16:42 +02:00
dynamicModelFile = std : : make_unique < DynamicModelMFile > ( fName , ntt ) ;
2009-05-27 16:28:23 +02:00
// intiate tensor library
2019-02-21 18:46:53 +01:00
TLStatic : : init ( kOrder , nStat + 2 * nPred + 3 * nBoth + 2 * nForw + nExog ) ;
2009-05-27 16:28:23 +02:00
// make KordpDynare object
2019-04-02 16:33:15 +02:00
KordpDynare dynare ( endoNames , exoNames , nExog , nPar ,
2009-05-27 16:28:23 +02:00
ySteady , vCov , modParams , nStat , nPred , nForw , nBoth ,
2019-04-02 16:33:15 +02:00
NNZD , nSteps , kOrder , journal , std : : move ( dynamicModelFile ) ,
2019-04-10 18:56:14 +02:00
dr_order , llincidence ) ;
2009-05-27 16:28:23 +02:00
2019-04-10 18:56:14 +02:00
// If model derivatives have been passed as arguments
if ( nrhs > 3 )
{
dynare . push_back_md ( prhs [ 3 ] ) ;
if ( nrhs > 4 )
dynare . push_back_md ( prhs [ 4 ] ) ;
if ( nrhs > 5 )
dynare . push_back_md ( prhs [ 5 ] ) ;
}
2009-12-16 18:18:38 +01:00
2019-04-10 18:56:14 +02:00
// construct main K-order approximation class
2019-03-07 18:17:43 +01:00
Approximation app ( dynare , journal , nSteps , false , qz_criterium ) ;
2009-05-27 16:28:23 +02:00
// run stochastic steady
app . walkStochSteady ( ) ;
2019-04-08 16:59:48 +02:00
const FoldDecisionRule & fdr = app . getFoldDecisionRule ( ) ;
2009-05-27 16:28:23 +02:00
2019-04-08 16:59:48 +02:00
// Add possibly missing field names
for ( int i = static_cast < int > ( g_fieldnames . size ( ) ) ; i < = kOrder ; i + + )
g_fieldnames . emplace_back ( " g_ " + std : : to_string ( i ) ) ;
// Create structure for storing derivatives in Dynare++ format
const char * g_fieldnames_c [ kOrder + 1 ] ;
for ( int i = 0 ; i < = kOrder ; i + + )
g_fieldnames_c [ i ] = g_fieldnames [ i ] . c_str ( ) ;
plhs [ 1 ] = mxCreateStructMatrix ( 1 , 1 , kOrder + 1 , g_fieldnames_c ) ;
2009-05-27 16:28:23 +02:00
2019-04-08 16:59:48 +02:00
// Fill that structure
for ( int i = 0 ; i < = kOrder ; i + + )
2009-05-27 16:28:23 +02:00
{
2019-04-08 16:59:48 +02:00
const FFSTensor & t = fdr . get ( Symmetry { i } ) ;
2019-04-16 12:40:50 +02:00
mxArray * tmp = mxCreateDoubleMatrix ( t . nrows ( ) , t . ncols ( ) , mxREAL ) ;
2019-04-08 16:59:48 +02:00
const ConstVector & vec = t . getData ( ) ;
2010-02-08 16:58:24 +01:00
assert ( vec . skip ( ) = = 1 ) ;
2019-04-08 16:59:48 +02:00
std : : copy_n ( vec . base ( ) , vec . length ( ) , mxGetPr ( tmp ) ) ;
mxSetField ( plhs [ 1 ] , 0 , ( " g_ " + std : : to_string ( i ) ) . c_str ( ) , tmp ) ;
2009-05-27 16:28:23 +02:00
}
2019-04-08 16:59:48 +02:00
if ( nlhs > 2 )
2009-05-27 16:28:23 +02:00
{
2019-04-08 16:59:48 +02:00
/* Return as 3rd argument a struct containing derivatives in Dynare
format ( unfolded matrices , without Taylor coefficient ) up to 3 rd
order */
const FGSContainer & derivs = app . get_rule_ders ( ) ;
size_t nfields = ( kOrder = = 1 ? 2 : ( kOrder = = 2 ? 6 : 12 ) ) ;
const char * c_fieldnames [ ] = { " gy " , " gu " , " gyy " , " gyu " , " guu " , " gss " ,
" gyyy " , " gyyu " , " gyuu " , " guuu " , " gyss " , " guss " } ;
plhs [ 2 ] = mxCreateStructMatrix ( 1 , 1 , nfields , c_fieldnames ) ;
copy_derivatives ( plhs [ 2 ] , Symmetry { 1 , 0 , 0 , 0 } , derivs , " gy " ) ;
copy_derivatives ( plhs [ 2 ] , Symmetry { 0 , 1 , 0 , 0 } , derivs , " gu " ) ;
if ( kOrder > = 2 )
2009-05-27 16:28:23 +02:00
{
2019-04-08 16:59:48 +02:00
copy_derivatives ( plhs [ 2 ] , Symmetry { 2 , 0 , 0 , 0 } , derivs , " gyy " ) ;
copy_derivatives ( plhs [ 2 ] , Symmetry { 0 , 2 , 0 , 0 } , derivs , " guu " ) ;
copy_derivatives ( plhs [ 2 ] , Symmetry { 1 , 1 , 0 , 0 } , derivs , " gyu " ) ;
copy_derivatives ( plhs [ 2 ] , Symmetry { 0 , 0 , 0 , 2 } , derivs , " gss " ) ;
2009-05-27 16:28:23 +02:00
}
2019-04-08 16:59:48 +02:00
if ( kOrder > = 3 )
2017-05-16 16:30:27 +02:00
{
2019-04-08 16:59:48 +02:00
copy_derivatives ( plhs [ 2 ] , Symmetry { 3 , 0 , 0 , 0 } , derivs , " gyyy " ) ;
copy_derivatives ( plhs [ 2 ] , Symmetry { 0 , 3 , 0 , 0 } , derivs , " guuu " ) ;
copy_derivatives ( plhs [ 2 ] , Symmetry { 2 , 1 , 0 , 0 } , derivs , " gyyu " ) ;
copy_derivatives ( plhs [ 2 ] , Symmetry { 1 , 2 , 0 , 0 } , derivs , " gyuu " ) ;
copy_derivatives ( plhs [ 2 ] , Symmetry { 1 , 0 , 0 , 2 } , derivs , " gyss " ) ;
copy_derivatives ( plhs [ 2 ] , Symmetry { 0 , 1 , 0 , 2 } , derivs , " guss " ) ;
2017-05-16 16:30:27 +02:00
}
}
2009-05-06 12:10:27 +02:00
}
2009-05-27 16:28:23 +02:00
catch ( const KordException & e )
2009-05-06 12:10:27 +02:00
{
2009-05-27 16:28:23 +02:00
e . print ( ) ;
2019-04-01 16:05:47 +02:00
DYN_MEX_FUNC_ERR_MSG_TXT ( ( " dynare:k_order_perturbation: Caught Kord exception: " + e . get_message ( ) ) . c_str ( ) ) ;
2009-05-06 12:10:27 +02:00
}
2009-05-27 16:28:23 +02:00
catch ( const TLException & e )
{
e . print ( ) ;
2010-09-20 16:57:02 +02:00
DYN_MEX_FUNC_ERR_MSG_TXT ( " dynare:k_order_perturbation: Caught TL exception " ) ;
2009-05-06 12:10:27 +02:00
}
2009-05-27 16:28:23 +02:00
catch ( SylvException & e )
{
e . printMessage ( ) ;
2010-09-20 16:57:02 +02:00
DYN_MEX_FUNC_ERR_MSG_TXT ( " dynare:k_order_perturbation: Caught Sylv exception " ) ;
2009-05-06 12:10:27 +02:00
}
2009-05-27 16:28:23 +02:00
catch ( const DynareException & e )
{
2019-04-01 16:05:47 +02:00
DYN_MEX_FUNC_ERR_MSG_TXT ( ( " dynare:k_order_perturbation: Caught KordDynare exception: " + e . message ( ) ) . c_str ( ) ) ;
2009-05-06 12:10:27 +02:00
}
2009-05-27 16:28:23 +02:00
catch ( const ogu : : Exception & e )
{
2019-04-01 16:05:47 +02:00
DYN_MEX_FUNC_ERR_MSG_TXT ( ( " dynare:k_order_perturbation: Caught general exception: " + e . message ( ) ) . c_str ( ) ) ;
2009-11-30 17:31:27 +01:00
}
2010-09-20 16:57:02 +02:00
plhs [ 0 ] = mxCreateDoubleScalar ( 0 ) ;
2009-11-30 17:31:27 +01:00
} // end of mexFunction()
} // end of extern C
2019-03-07 18:17:43 +01:00