// Main Entry Function // ----------------------------------------------------------------- void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { //Input Args usrFcn fun, con; double *x0, *lb = NULL, *ub = NULL; char *xtype = NULL; //Outputs Args double *x, *fval, *exitflag, *iter, *nfval; //Internal Vars size_t ndec; int i, nobj = 1, ncon = 0; char errstr[1024]; //used for returning error info iter_fun_data iterF; //Check user inputs if(!checkInputs(prhs,nrhs,plhs,nlhs)) return; //Redirect cout printfbuf buf; std::streambuf *cout_sbuf = std::cout.rdbuf(); //keep existing buffer std::cout.rdbuf(&buf); //redirect buffer //NOMAD Vars NOMAD::Mads *mads; NOMAD::Display out(std::cout); NOMAD::Parameters p(out); NOMAD::Point px0; NOMAD::Double *nx0; NOMAD::stop_type stopflag; //Evaluator Vars matlabEval *mSEval = NULL; matlabMEval *mBEval = NULL; //Set Option Defaults int printLevel = 0; char *paramfile = NULL; mxArray *bb_out_type = NULL; iterF.enabled = false; //Get Size ndec = mxGetNumberOfElements(pX0); //Get Blackbox / Objective Function Handle if (mxIsChar(pFUN)) { if(mxGetString(pFUN, fun.f, FLEN) != 0) mexErrMsgTxt("error reading objective name string"); fun.nrhs = 1; fun.xrhs = 0; } else { fun.prhs[0] = (mxArray*)pFUN; strcpy(fun.f, "feval"); fun.nrhs = 2; fun.xrhs = 1; } fun.prhs[fun.xrhs] = mxCreateDoubleMatrix(ndec, 1, mxREAL); //x //Get x0 x0 = mxGetPr(pX0); //Get xtype if(nrhs > eXTYPE && !mxIsEmpty(pXTYPE)) xtype = mxArrayToString(pXTYPE); //Get MEX Options if specified if(nrhs > eOPTS && !mxIsEmpty(pOPTS)) { if(mxGetField(pOPTS,0,"display_degree") && !mxIsEmpty(mxGetField(pOPTS,0,"display_degree"))) printLevel = (int)*mxGetPr(mxGetField(pOPTS,0,"display_degree")); if(mxGetField(pOPTS,0,"param_file") && !mxIsEmpty(mxGetField(pOPTS,0,"param_file"))) paramfile = mxArrayToString(mxGetField(pOPTS,0,"param_file")); if(mxGetField(pOPTS,0,"bb_output_type") && !mxIsEmpty(mxGetField(pOPTS,0,"bb_output_type"))) bb_out_type = mxGetField(pOPTS,0,"bb_output_type"); if(mxGetField(pOPTS,0,"iterfun") && !mxIsEmpty(mxGetField(pOPTS,0,"iterfun"))) { iterF.prhs[0] = (mxArray*)mxGetField(pOPTS,0,"iterfun"); strcpy(iterF.f, "feval"); iterF.enabled = true; iterF.prhs[1] = mxCreateNumericMatrix(1,1,mxINT32_CLASS,mxREAL); iterF.prhs[2] = mxCreateDoubleMatrix(1,1,mxREAL); iterF.prhs[3] = mxCreateDoubleMatrix(ndec,1,mxREAL); } } //Create Outputs (note x and fval created below, due to allowing bi-objective) plhs[2] = mxCreateDoubleMatrix(1,1, mxREAL); plhs[3] = mxCreateDoubleMatrix(1,1, mxREAL); plhs[4] = mxCreateDoubleMatrix(1,1, mxREAL); exitflag = mxGetPr(plhs[2]); iter = mxGetPr(plhs[3]); nfval = mxGetPr(plhs[4]); //Setup ndec p.set_DIMENSION((int)ndec); //Warn if >1000 if(ndec > 1000 && printLevel) { sprintf(errstr,"Warning: NOMAD is designed problems less than 1000 variables. Your model has %d.\nWhile unlikely, it is possible NOMAD may not perform as intended on this problem.",static_cast<int>(ndec)); mexWarnMsgTxt(errstr); } //Setup Lower Bounds if(nrhs > eLB && !mxIsEmpty(pLB)) { NOMAD::Point ptLB; NOMAD::Double *dLB = new NOMAD::Double[ndec]; lb = mxGetPr(pLB); for(i=0;i<ndec;i++) { if(!mxIsInf(lb[i])) //if not initialized will not be used dLB[i] = lb[i]; } ptLB.set((int)ndec,dLB); p.set_LOWER_BOUND(ptLB); delete [] dLB; } //Setup Upper Bounds if(nrhs > eUB && !mxIsEmpty(pUB)) { NOMAD::Point ptUB; NOMAD::Double *dUB = new NOMAD::Double[ndec]; ub = mxGetPr(pUB); for(i=0;i<ndec;i++) { if(!mxIsInf(ub[i])) //if not initialized will not be used dUB[i] = ub[i]; } ptUB.set((int)ndec,dUB); p.set_UPPER_BOUND(ptUB); delete [] dUB; } //Setup x0 nx0 = new NOMAD::Double[ndec]; #ifdef OPTI_VERSION double xl, xu; //If integer variables declared, need to ensure x0[i] is an integer if(xtype) { for(i=0;i<ndec;i++) { switch(tolower(xtype[i])) { case 'c': //Ensure within bounds if(lb && x0[i] < lb[i]) nx0[i] = lb[i]; else if(ub && x0[i] > ub[i]) nx0[i] = ub[i]; else nx0[i] = x0[i]; //no rounding break; case 'i': case 'b': xl = floor(x0[i]); //First round is a floor //If lower bounds exist if(lb) { //if lower bound broken if(xl < lb[i]) { xu = ceil(x0[i]); //If upper bounds exist, check bound directions if(ub && xu > ub[i]) { //if broken, no integer x0 exists sprintf(errstr,"x0[%d] cannot be rounded to an integer value between lb: %g, ub %g",i,lb[i],ub[i]); mexErrMsgTxt(errstr); } if(xu != x0[i]) { //If we changed something, warn user sprintf(errstr,"x0[%d] was rounded up to %g to suit NOMAD interface",i,xu); mexWarnMsgTxt(errstr); } //Save ceil value nx0[i] = xu; } //Floor value did not break lower bounds, value OK else { if(xl != x0[i]) { //If we changed something, warn user sprintf(errstr,"x0[%d] was rounded down to %g to suit NOMAD interface",i,xl); mexWarnMsgTxt(errstr); } //Save floor value nx0[i] = xl; } } //No lower bounds, floor value assumed OK else { if(xl != x0[i]) { //If we changed something, warn user sprintf(errstr,"x0[%d] was rounded down to %g to suit NOMAD interface",i,xl); mexWarnMsgTxt(errstr); } //Save floor value nx0[i] = xl; } break; case 'r': mexErrMsgTxt("Please specify continuous (real) variables using 'c' (as opposed to 'r') when using the OPTI version"); break; default: sprintf(errstr,"Unknown xtype[%d] character: %c\n\nValid options are 'C', 'I', or 'B'\n",i,xtype[i]); mexErrMsgTxt(errstr); } } } //Else user start position within bounds else { for(i=0;i<ndec;i++) { if(lb && x0[i] < lb[i]) nx0[i] = lb[i]; else if(ub && x0[i] > ub[i]) nx0[i] = ub[i]; else nx0[i] = x0[i]; } } #else //GERAD VERSION - no x0 checking for(i=0;i<ndec;i++) nx0[i] = x0[i]; #endif px0.set((int)ndec,nx0); p.set_X0(px0); delete [] nx0; #ifdef OPTI_VERSION //Setup Nonlinear Constraints if(nrhs > eNLCON && !mxIsEmpty(pNLCON)) { if (mxIsChar(pNLCON)) { if(mxGetString(pNLCON, con.f, FLEN) != 0) mexErrMsgTxt("error reading constraint name string"); con.nrhs = 1; con.xrhs = 0; } else { con.prhs[0] = (mxArray*)pNLCON; strcpy(con.f, "feval"); con.nrhs = 2; con.xrhs = 1; } con.prhs[con.xrhs] = mxCreateDoubleMatrix(ndec, 1, mxREAL); //x if(nrhs < eNLRHS+1 || mxIsEmpty(pNLRHS)) {//we will default to <= 0 ncon = -1; con.nlrhs = NULL; } else { ncon = (int)mxGetNumberOfElements(pNLRHS); con.nlrhs = mxGetPr(pNLRHS); } } //Setup Input Variable Types if(xtype) p.set_BB_INPUT_TYPE(detInTypes(xtype,ndec)); //Setup Evaluation Return Types + #'s of Obj + Con p.set_BB_OUTPUT_TYPE(detRetTypes(&fun,bb_out_type,&nobj,&con,&ncon,x0,ndec)); //Set All Normal NOMAD Options p.set_DISPLAY_DEGREE(0); //default #endif //GERAD Version does not have a separate constraint handler and handles input and output types using options //Set User Options if(nrhs > eOPTS && !mxIsEmpty(pOPTS)) setNOMADopts(p,pOPTS); else setNOMADopts(p,NULL); //If the user has specified a parameter file to read, see if it exists, and if so, read and parse it. if(paramfile) { FILE *pFile = fopen(paramfile,"r"); if(pFile==NULL) { sprintf(errstr,"Cannot open parameter file: %s\n\nEnsure it exists!",paramfile); mexErrMsgTxt(errstr); } else{ fclose(pFile); //close file pointer (we don't need it) try { p.read(paramfile); } catch(exception &e) { sprintf(errstr,"NOMAD Parameter File Read Error:\n\n%s",e.what()); mexErrMsgTxt(errstr); } } } //Check NOMAD parameters try { p.check(); } catch(exception &e) { sprintf(errstr,"NOMAD Parameter Error:\n\n%s",e.what()); mexErrMsgTxt(errstr); } //Check for categorical variables if (p.get_signature()->has_categorical()) mexErrMsgTxt("The MATLAB version of NOMAD does not support categorical variables yet! Check BB_INPUT_TYPE parameter."); //If GERAD version, obtain number of objectives and constraints from parameters #ifndef OPTI_VERSION nobj=p.get_nb_obj(); ncon=(int)p.get_bb_output_type().size()-nobj; #endif //If user has requested surrogates, setup extra input arg to callbacks if (p.has_sgte()) { fun.prhs[fun.xrhs+1] = mxCreateLogicalMatrix(1,1); //extra logical indicating surrogate or not fun.nrhs++; #ifdef OPTI_VERSION con.prhs[con.xrhs+1] = mxCreateLogicalMatrix(1,1); con.nrhs++; #endif } //Print Header if(printLevel) { mexPrintf("\n------------------------------------------------------------------\n"); mexPrintf(" This is NOMAD v%s\n",NOMAD::VERSION.c_str()); mexPrintf(" Authors: M. Abramson, C. Audet, G. Couture, J. Dennis, S. Le Digabel, C. Tribes\n\n"); mexPrintf(" Problem Properties:\n"); mexPrintf(" # Decision Variables: %4d\n",ndec); mexPrintf(" # Number of Objectives: %4d\n",nobj); mexPrintf(" # Number of Nonlinear Constraints: %4d\n",ncon); mexPrintf("------------------------------------------------------------------\n"); mexEvalString("drawnow;"); //flush draw buffer } //Let this file sort out errors mexSetTrapFlag(1); //Reset tags and bbe (C.Tribes 3/14) NOMAD::Eval_Point::reset_tags_and_bbes(); //Create evaluator and run mads based on number of objectives try { if(nobj > 1) { mBEval = new matlabMEval(p,&fun,nobj,&con,ncon,&iterF); //Bi-Objective Evaluator mads = new NOMAD::Mads(p, mBEval); //Run NOMAD stopflag = mads->multi_run(); } else { mSEval = new matlabEval(p,&fun,nobj,&con,ncon,&iterF); //Single Objective Evaluator mads = new NOMAD::Mads(p, mSEval); //Run NOMAD stopflag = mads->run(); } } catch(exception &e) { //Free Memory (C.Tribes 3/14) if(mSEval) delete mSEval; mSEval = NULL; if(mBEval) delete mBEval; mBEval = NULL; delete mads; if(xtype) mxFree(xtype); xtype = NULL; //Report Error sprintf(errstr,"NOMAD Run Error:\n\n%s",e.what()); mexErrMsgTxt(errstr); } if(printLevel) mexPrintf("------------------------------------------------------------------\n"); //Obtain Solution based on Single or Multi-Objective (C.Tribes oct 09, 2013 --- changed to deal with pareto output) if(nobj>1) { NOMAD::Pareto_Front * pareto_front=mads->get_pareto_front(); if (pareto_front) { int nb_pareto_pts = pareto_front->size(); plhs[0] = mxCreateDoubleMatrix(ndec,nb_pareto_pts, mxREAL); plhs[1] = mxCreateDoubleMatrix(nobj,nb_pareto_pts, mxREAL); x = mxGetPr(plhs[0]); fval = mxGetPr(plhs[1]); const NOMAD::Eval_Point * cur = pareto_front->begin(); int i=0; while ( cur ) { //mexPrintf("i %d OK: %d FEAS: %d\n",i,cur->is_eval_ok(),cur->is_feasible ( p.get_h_min() )); if ( cur->is_eval_ok() && cur->is_feasible ( p.get_h_min() ) ) { const std::list<int> & index_obj = p.get_index_obj(); std::list<int>::const_iterator it , end = index_obj.end(); const NOMAD::Point & bbo = cur->get_bb_outputs(); int j = 0; NOMAD::Point multi_obj ( static_cast<int>(index_obj.size()) ); for ( it = index_obj.begin() ; it != end ; ++it,j++ ) fval[nobj*i+j] = bbo[*it].value(); for(j=0;j<ndec;j++) x[ndec*i+j] = (*cur)[j].value(); } cur = pareto_front->next(); i++; } *exitflag = getStatus(stopflag); } else { stopflag = (NOMAD::stop_type)10; *exitflag = -1; //No solution } } else { //Create single objective outputs plhs[0] = mxCreateDoubleMatrix(ndec,1, mxREAL); plhs[1] = mxCreateDoubleMatrix(1,1, mxREAL); x = mxGetPr(plhs[0]); fval = mxGetPr(plhs[1]); const NOMAD::Eval_Point *bestSol = mads->get_best_feasible(); if(bestSol == NULL) { bestSol = mads->get_best_infeasible(); //manually set as infeasible (no infeasible stop flag) stopflag = (NOMAD::stop_type)10; } if(bestSol == NULL) *exitflag = -1; //No solution //If we have a solution, save it if(*exitflag != -1) { //Save x NOMAD::Point pt(*bestSol); for(i=0;i<ndec;i++) x[i] = pt[i].value(); //Save fval *fval = bestSol->get_f().value(); //Save Status *exitflag = getStatus(stopflag); } } //Common outputs *iter = mads->get_stats().get_iterations(); *nfval = mads->get_stats().get_bb_eval(); //Return cout to initial buffer std::cout.rdbuf(cout_sbuf); //Return error control to default mexSetTrapFlag(0); //Free Memory if(mSEval) delete mSEval; mSEval = NULL; if(mBEval) delete mBEval; mBEval = NULL; delete mads; if(xtype) mxFree(xtype); xtype = NULL; }
/*-------------------------------------------------------------------*/ bool NOMAD::SMesh::get_Delta ( NOMAD::Point & Delta ) const { Delta.reset ( _n ); // power_of_tau = tau^{ max{0,l0} - max{0,lk} + |lk|/2}: NOMAD::Double power_of_tau = pow ( _update_basis.value() , abs(_mesh_index) / 2.0 + ( (_initial_mesh_index > 0) ? _initial_mesh_index : 0) - ( (_mesh_index > 0) ? _mesh_index : 0) ); bool stop = true; bool mps_def = _Delta_min.is_complete(); // Delta^k = power_of_tau * Delta^0: for ( int i = 0 ; i < _n ; ++i ) { Delta[i] = _Delta_0[i] * power_of_tau; if ( !mps_def || Delta[i] >= _Delta_min[i] ) stop = false; if ( _Delta_min.is_defined() && _Delta_min[i].is_defined() && Delta[i] < _Delta_min[i] ) Delta[i]=_Delta_min[i]; } return stop; }
ArrayXd CMADS::NOMADPoint2ArrayXd(const NOMAD::Point &p) { int i,n=p.size(); ArrayXd x(n); for ( i=0; i<n; i++ ) x(i)=p[i].value(); return x; }
/*-----------------------------------------------------------*/ void NOMAD::SMesh::update ( NOMAD::success_type success , NOMAD::Point & mesh_indices, const NOMAD::Direction *dir ) const { if ( mesh_indices.is_defined() ) { for (int i=0; i < mesh_indices.size() ; i++) { if ( success == NOMAD::FULL_SUCCESS ) { mesh_indices[i] -= _coarsening_step; if ( mesh_indices[i] < -NOMAD::L_LIMITS ) mesh_indices[i] = -NOMAD::L_LIMITS; } else if ( success == NOMAD::UNSUCCESSFUL ) mesh_indices[i] -= _refining_step; } } }
/*---------------------------------------------------------*/ bool NOMAD::LH_Search::LH_points ( int n , int m , int p , const NOMAD::Point & lb , const NOMAD::Point & ub , std::vector<NOMAD::Eval_Point *> & pts ) { if ( n <= 0 || p <= 0 || !lb.is_defined() || !ub.is_defined() || lb.size() != n || ub.size() != n ) return false; for ( size_t j = 0 ; j < pts.size() ; ++j ) delete pts[j]; pts.clear(); NOMAD::Eval_Point * x; int i; int pm1 = p-1; NOMAD::Random_Pickup ** rps = new NOMAD::Random_Pickup *[n]; for ( int k = 0 ; k < p ; ++k ) { x = new NOMAD::Eval_Point ( n , m ); for ( i = 0 ; i < n ; ++i ) { if ( k==0 ) rps[i] = new NOMAD::Random_Pickup(p); (*x)[i] = lb[i] + (ub[i]-lb[i]) * ( rps[i]->pickup() + NOMAD::RNG::rand()/(1.0+NOMAD::D_INT_MAX)) / p; if ( k==pm1 ) delete rps[i]; } pts.push_back(x); } delete [] rps; return true; }
NOMAD::Double NOMAD::SMesh::scale_and_project(int i, NOMAD::Double l) const { NOMAD::Point delta; NOMAD::Point Delta; get_delta ( delta ); get_Delta ( Delta ); if ( delta.is_defined() && Delta.is_defined() && i <= _n) { NOMAD::Double d= Delta[i] / delta[i] * l; return d.NOMAD::Double::round()*delta[i]; } else throw NOMAD::Exception ( "SMesh.cpp" , __LINE__ , "Mesh scaling and projection cannot be performed!" ); }
/*-----------------------------------------------------------*/ void NOMAD::SMesh::set_mesh_indices ( const NOMAD::Point & r ) { if (!r.is_defined()) _mesh_index=0; else _mesh_index=r[0].NOMAD::Double::round(); if ( _mesh_index > _max_mesh_index ) _max_mesh_index = _mesh_index; if ( _mesh_index < _min_mesh_index ) _min_mesh_index = _mesh_index; }
/*---------------------------------------------------------*/ NOMAD::Model_Sorted_Point::Model_Sorted_Point ( NOMAD::Point * x , const NOMAD::Point & center ) : _x(x) { int i , n = center.size(); if ( x && x->size() == n ) { _dist = 0.0; for ( i = 0 ; i < n ; ++i ) if ( (*x)[i].is_defined() && center[i].is_defined() ) { _dist += ( (*x)[i] - center[i] ).pow2(); } else { _dist.clear(); break; } } }
/*----------------------------------------------------------------*/ bool NOMAD::SMesh::get_delta ( NOMAD::Point & delta ) const { delta.reset ( _n ); // power_of_tau = tau^{ max{0,l0} - max{0,lk} }: NOMAD::Double power_of_tau = pow ( _update_basis.value() , ( (_initial_mesh_index > 0) ? _initial_mesh_index : 0) - ( (_mesh_index > 0) ? _mesh_index : 0) ); bool stop = false; // delta^k = power_of_tau * delta^0: for ( int i = 0 ; i < _n ; ++i ) { delta[i] = _delta_0[i] * power_of_tau; if ( !stop && _delta_min.is_defined() && delta[i] < _delta_min[i] ) stop = true; } return stop; }
/*----------------------------------------------------------------*/ void NOMAD::TGP_Model::eval_hf(const NOMAD::Point &bbo , const NOMAD::Double &h_min , NOMAD::hnorm_type h_norm , NOMAD::Double &h , NOMAD::Double &f) const { f.clear(); h = 0.0; int m = bbo.size(); if (m != static_cast<int>(_bbot.size())) throw NOMAD::Exception("TGP_Model.cpp" , __LINE__ , "TGP_Model::eval_hf() called with an invalid bbo argument"); NOMAD::Double bboi; for (int i = 0 ; i < m ; ++i) { bboi = bbo[i]; if (bboi.is_defined()) { if (_bbot[i] == NOMAD::EB || _bbot[i] == NOMAD::PEB_E) { if (bboi > h_min) { h.clear(); return; } } else if ((_bbot[i] == NOMAD::FILTER || _bbot[i] == NOMAD::PB || _bbot[i] == NOMAD::PEB_P)) { if (bboi > h_min) { switch (h_norm) { case NOMAD::L1: h += bboi; break; case NOMAD::L2: h += bboi * bboi; break; case NOMAD::LINF: if (bboi > h) h = bboi; break; } } } else if (_bbot[i] == NOMAD::OBJ) f = bboi; } } if (h_norm == NOMAD::L2) h = h.sqrt(); }
/*---------------------------------------------------------*/ void NOMAD::Variable_Group::get_directions ( std::list<NOMAD::Direction> & dirs , NOMAD::poll_type poll , const NOMAD::Point & poll_center , int mesh_index , const NOMAD::Direction & feas_success_dir , const NOMAD::Direction & infeas_success_dir ) { int nc = static_cast<int>(_var_indexes.size()); // vectors are of size nc : // ------------------------ if ( nc == poll_center.size() ) { _directions->compute ( dirs , poll , poll_center , mesh_index , -1 , feas_success_dir , infeas_success_dir ); return; } // construct vectors of size nc from vectors of size < nc : // -------------------------------------------------------- std::set<int>::const_iterator it ; int i = 0; NOMAD::Point new_poll_center ( nc ); NOMAD::Direction new_fsd , new_isd; if ( feas_success_dir.is_defined() ) new_fsd = NOMAD::Direction ( nc , 0.0 , feas_success_dir.get_type() ); if ( infeas_success_dir.is_defined() ) new_isd = NOMAD::Direction ( nc , 0.0 , infeas_success_dir.get_type() ); for ( it = _var_indexes.begin() ; it != _var_indexes.end() ; ++it ) { new_poll_center[i] = poll_center[*it]; if ( feas_success_dir.is_defined() ) new_fsd[i] = feas_success_dir [*it]; if ( infeas_success_dir.is_defined() ) new_isd[i] = infeas_success_dir[*it]; ++i; } _directions->compute ( dirs , poll , new_poll_center , mesh_index , -1 , new_fsd , new_isd ); }