/*-------------------------------------------------------------------------------------*/ /* NOMAD - Nonlinear Optimization by Mesh Adaptive Direct search - version 3.6.1 */ /* */ /* Copyright (C) 2001-2012 Mark Abramson - the Boeing Company, Seattle */ /* Charles Audet - Ecole Polytechnique, Montreal */ /* Gilles Couture - Ecole Polytechnique, Montreal */ /* John Dennis - Rice University, Houston */ /* Sebastien Le Digabel - Ecole Polytechnique, Montreal */ /* Christophe Tribes - Ecole Polytechnique, Montreal */ /* */ /* funded in part by AFOSR and Exxon Mobil */ /* */ /* Author: Sebastien Le Digabel */ /* */ /* Contact information: */ /* Ecole Polytechnique de Montreal - GERAD */ /* C.P. 6079, Succ. Centre-ville, Montreal (Quebec) H3C 3A7 Canada */ /* e-mail: nomad@gerad.ca */ /* phone : 1-514-340-6053 #6928 */ /* fax : 1-514-340-5665 */ /* */ /* This program is free software: you can redistribute it and/or modify it under the */ /* terms of the GNU Lesser General Public License as published by the Free Software */ /* Foundation, either version 3 of the License, or (at your option) any later */ /* version. */ /* */ /* This program 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 Lesser General Public License for more details. */ /* */ /* You should have received a copy of the GNU Lesser General Public License along */ /* with this program. If not, see . */ /* */ /* You can find information on the NOMAD software at www.gerad.ca/nomad */ /*-------------------------------------------------------------------------------------*/ /** \file TGP_Model_Search.cpp \brief TGP Model search (implementation) \author Sebastien Le Digabel \date 2011-02-17 \see TGP_Model_Search.hpp */ #ifndef USE_TGP int TGP_MODEL_SEARCH_DUMMY; // avoids that TGP_Model_Search.o has no symbols with ranlib #else #include "TGP_Model_Search.hpp" /*-----------------------------------*/ /* reset (virtual) */ /*-----------------------------------*/ void NOMAD::TGP_Model_Search::reset ( void ) { if ( _model ) delete _model; _model = NULL; } /*--------------------------------------------------------*/ /* delete a list of points: one version for points, and */ /* one version for evaluation points (static, private) */ /*--------------------------------------------------------*/ void NOMAD::TGP_Model_Search::clear_pts ( std::vector & pts ) { size_t k , n = pts.size(); for ( k = 0 ; k < n ; ++k ) delete pts[k]; pts.clear(); } void NOMAD::TGP_Model_Search::clear_pts ( std::vector & pts ) { size_t k , n = pts.size(); for ( k = 0 ; k < n ; ++k ) delete pts[k]; pts.clear(); } /*------------------------------------------------------------------*/ /* the search */ /*------------------------------------------------------------------*/ /* Search parameters: */ /* ------------------ */ /* */ /* . MODEL_SEARCH: flag to activate the model search (MS) */ /* (here its value is NOMAD::TGP_MODEL) */ /* */ /* . MODEL_SEARCH_OPTIMISTIC: if true, the direction from the */ /* model center to the trial point */ /* is computed and prossibly used */ /* in the speculative search */ /* default=yes */ /* */ /* . MODEL_SEARCH_PROJ_TO_MESH: project or not to mesh */ /* default=yes */ /* */ /* . MODEL_SEARCH_MAX_TRIAL_PTS: limit on the number of trial */ /* points for one search */ /* default=10 */ /* */ /* . MODEL_TGP_MODE: TGP mode (FAST or PRECISE) */ /* default=FAST */ /* */ /*------------------------------------------------------------------*/ void NOMAD::TGP_Model_Search::search ( NOMAD::Mads & mads , int & nb_search_pts , bool & stop , NOMAD::stop_type & stop_reason , NOMAD::success_type & success , bool & count_search , const NOMAD::Eval_Point *& new_feas_inc , const NOMAD::Eval_Point *& new_infeas_inc ) { new_feas_inc = new_infeas_inc = NULL; nb_search_pts = 0; success = NOMAD::UNSUCCESSFUL; count_search = false; _one_search_stats.reset(); const NOMAD::Display & out = _p.out(); NOMAD::dd_type display_degree = out.get_search_dd(); int display_lim = 15; if ( stop ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << "TGP_Model_Search::search(): not performed (stop flag is active)" << std::endl; return; } // active cache (we accept only true function evaluations): const NOMAD::Cache & cache = mads.get_cache(); if ( cache.get_eval_type() != NOMAD::TRUTH ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << "TGP_Model_Search::search(): not performed on surrogates" << std::endl; return; } // check that there is one objective exactly: const std::list & index_obj_list = _p.get_index_obj(); if ( index_obj_list.empty() ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << "TGP_Model_Search::search(): not performed with no objective function" << std::endl; return; } if ( index_obj_list.size() > 1 ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << "TGP_Model_Search::search(): not performed with biobjective" << std::endl; return; } // active barrier: const NOMAD::Barrier & barrier = mads.get_true_barrier(); // get the incumbent: const NOMAD::Eval_Point * incumbent = barrier.get_best_feasible(); if ( !incumbent ) incumbent = barrier.get_best_infeasible(); if ( !incumbent ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << "TGP_Model_Search::search(): no incumbent" << std::endl; return; } // get and check the signature, and compute the dimension: NOMAD::Signature * signature = incumbent->get_signature(); if ( !signature ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << "TGP_Model_Search::search(): no signature" << std::endl; return; } int n = signature->get_n(); if ( n != incumbent->size() ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << "TGP_Model_Search::search(): incompatible signature" << std::endl; return; } // initial displays: if ( display_degree == NOMAD::FULL_DISPLAY ) { std::ostringstream oss; oss << NOMAD::MODEL_SEARCH << " #" << _all_searches_stats.get_MS_nb_searches(); out << std::endl << NOMAD::open_block ( oss.str() ) << std::endl; } // from this point the search is counted: count_search = true; _one_search_stats.add_MS_nb_searches(); // mesh: int mesh_index = NOMAD::Mesh::get_mesh_index(); NOMAD::Point delta_m; signature->get_mesh().get_delta_m ( delta_m , mesh_index ); // initial displays: if ( display_degree == NOMAD::FULL_DISPLAY ) { #ifdef TGP_DEBUG out << "seed : " << _p.get_seed() << std::endl; #endif out << "number of cache points: " << cache.size() << std::endl << "mesh index : " << mesh_index << std::endl << "mesh size parameter : ( " << delta_m << " )" << std::endl << "incumbent : ( "; incumbent->NOMAD::Point::display ( out , " " , 2 , NOMAD::Point::get_display_limit() ); out << " )" << std::endl; } // construct the model: NOMAD::Stats & stats = mads.get_stats(); bool compute_Ds2x; std::vector XX; std::string error_str; if ( !model_construction ( cache , *incumbent , delta_m , out , display_degree , display_lim , stats , compute_Ds2x , XX , stop , stop_reason , error_str ) ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << NOMAD::close_block ( "failure: " + error_str ) << std::endl; return; } // trial_pts = oracle_pts + Ds2x_pts + improv_pts // // oracle_pts: given by the model optimization // Ds2x_pts : XX points with the largest expected reduction in predictive variance // improv_pts: XX points with the largest expected improvement for the objective int max_pts = _p.get_model_search_max_trial_pts(); /*-----------------------*/ /* oracle points (1/3) */ /*-----------------------*/ std::vector oracle_pts; if ( !create_oracle_pts ( cache , *incumbent , delta_m , out , display_degree , display_lim , XX , oracle_pts , stop , stop_reason ) && stop ) { // delete XX and oracle_pts: NOMAD::TGP_Model_Search::clear_pts ( XX ); NOMAD::TGP_Model_Search::clear_pts ( oracle_pts ); // quit: if ( display_degree == NOMAD::FULL_DISPLAY ) out << NOMAD::close_block ( "algorithm stop" ) << std::endl; return; } /*---------------------*/ /* Ds2x points (2/3) */ /*---------------------*/ std::vector Ds2x_pts; if ( compute_Ds2x ) create_Ds2x_pts ( XX , out , display_degree , display_lim , Ds2x_pts ); /*-----------------------*/ /* improv points (3/3) */ /*-----------------------*/ std::vector improv_pts; create_improv_pts ( XX , *incumbent , max_pts , out , display_degree , display_lim , improv_pts ); // create the complete list of trial points: // ----------------------------------------- std::vector trial_pts; create_trial_pts ( oracle_pts , Ds2x_pts , improv_pts , *incumbent , max_pts , out , display_degree , trial_pts ); // evaluator control: NOMAD::Evaluator_Control & ev_control = mads.get_evaluator_control(); // add the trial points to the evaluator control for evaluation: int i , nop = trial_pts.size(); for ( i = 0 ; i < nop ; ++i ) register_point ( *trial_pts[i] , *signature , *incumbent , mesh_index , display_degree , ev_control ); // display the evaluator control list of points: if ( display_degree == NOMAD::FULL_DISPLAY ) { out << std::endl << NOMAD::open_block ( "list of trial points" ); const std::set & lop = ev_control.get_eval_lop(); std::set::const_iterator it , end = lop.end(); nop = lop.size(); for ( it = lop.begin() , i = 0 ; it != end ; ++it , ++i ) { out << "#"; out.display_int_w ( i , nop ); out << " x=( "; it->get_point()->NOMAD::Point::display ( out , " " , 15 , -1 ); out << " )" << std::endl; } out.close_block(); } // delete XX and the trial points // (do not delete Ds2x_pts and improv_pts because // they are XX points, contrary to oracle_pts): NOMAD::TGP_Model_Search::clear_pts ( XX ); NOMAD::TGP_Model_Search::clear_pts ( oracle_pts ); nb_search_pts = ev_control.get_nb_eval_points(); if ( nb_search_pts == 0 ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << "no trial point" << std::endl; } else { _one_search_stats.update_MS_max_search_pts ( nb_search_pts ); // evaluate the trial points: // -------------------------- int bbe = stats.get_bb_eval(); int sgte_eval = stats.get_sgte_eval (); int cache_hits = stats.get_cache_hits(); new_feas_inc = new_infeas_inc = NULL; ev_control.disable_model_eval_sort(); std::list * evaluated_pts = NULL; if ( display_degree == NOMAD::FULL_DISPLAY ) evaluated_pts = new std::list; ev_control.eval_list_of_points ( _type , mads.get_true_barrier() , mads.get_sgte_barrier() , mads.get_pareto_front() , stop , stop_reason , new_feas_inc , new_infeas_inc , success , evaluated_pts ); // display the prediction error for the evaluated points: if ( display_degree == NOMAD::FULL_DISPLAY ) { display_eval_pred_errors ( *evaluated_pts , out ); delete evaluated_pts; } ev_control.enable_model_eval_sort(); // update stats: _one_search_stats.add_MS_bb_eval ( stats.get_bb_eval () - bbe ); _one_search_stats.add_MS_sgte_eval ( stats.get_sgte_eval () - sgte_eval ); _one_search_stats.add_MS_cache_hits ( stats.get_cache_hits() - cache_hits ); if ( success == NOMAD::FULL_SUCCESS ) _one_search_stats.add_MS_success(); _one_search_stats.add_MS_pts ( nb_search_pts ); } // update stats objects: stats.update_model_stats ( _one_search_stats ); _all_searches_stats.update ( _one_search_stats ); // final display: if ( display_degree == NOMAD::FULL_DISPLAY ) { std::ostringstream oss; oss << "end of " << NOMAD::MODEL_SEARCH << " (" << success << ")"; out << std::endl << NOMAD::close_block ( oss.str() ) << std::endl; } } /*---------------------------------------------------------------*/ /* create XX a list of prediction points (private) */ /*---------------------------------------------------------------*/ void NOMAD::TGP_Model_Search::set_XX ( const NOMAD::Cache & cache , int n , int m , const NOMAD::Point & incumbent , const NOMAD::Point & delta_m , std::vector & XX ) const { // . we begin with 1999 points, filter them to remove points // that appear more than once or in the cache. // . then we prune the list to 499 points. // . we do not begin directly with 500 points because it // gives more flexibility with the projection. // . we terminate by adding the incumbent. // --> we obtain a list of at most 500 prediction points. int j , i = 0 , n_XX = 1999; // 2000-1 bool proj_to_mesh = _p.get_model_search_proj_to_mesh(); bool remove_pt; // XX is made of n_XX-1 LH points inside the model bounds // (and the incumbent will be added at the end): NOMAD::LH_Search::LH_points ( n, m, n_XX, _model->get_lb(), _model->get_ub(), XX ); while ( i < n_XX ) { remove_pt = false; // project to the mesh: if ( proj_to_mesh ) XX[i]->project_to_mesh ( incumbent , delta_m , _p.get_lb() , _p.get_ub() ); // remove if point is in cache: if ( cache.find ( *XX[i] ) ) remove_pt = true; // check if this point is already in XX // (may occur only if the point has been projected): if ( proj_to_mesh && !remove_pt ) { if ( incumbent == (*XX[i]) ) remove_pt = true; else for ( j = 0 ; j < i ; ++j ) if ( XX[j]->NOMAD::Point::operator == ( *XX[i] ) ) { remove_pt = true; break; } } // remove the point: if ( remove_pt ) { delete XX[i]; --n_XX; if ( i != n_XX ) XX[i] = XX[n_XX]; XX.resize ( n_XX ); } else ++i; } // reduce to 500-1 points (we eliminate randomly): while ( n_XX >= 500 ) { i = rand()%n_XX; delete XX[i]; --n_XX; if ( i != n_XX ) XX[i] = XX[n_XX]; XX.resize(n_XX); } // add the incumbent as the last point of XX: XX.push_back ( new NOMAD::Eval_Point ( n , m ) ); XX[n_XX]->NOMAD::Point::operator = ( incumbent ); } /*---------------------------------------------------------------------*/ /* create the complete list of trial points (oracle + Ds2x + improv) */ /* (private) */ /*---------------------------------------------------------------------*/ void NOMAD::TGP_Model_Search::create_trial_pts ( const std::vector & oracle_pts , // IN const std::vector & Ds2x_pts , // IN const std::vector & improv_pts , // IN const NOMAD::Point & incumbent , // IN int max_pts , // IN const NOMAD::Display & out , // IN NOMAD::dd_type display_degree , // IN std::vector & trial_pts ) const // OUT { bool found; size_t i , j , n1 = oracle_pts.size() , n2 = Ds2x_pts.size () , n3 = improv_pts.size(); std::vector l2 , l3; // 1. remove duplicates: // --------------------- // // . oracle_pts are not XX points // . Ds2x_pts and improv_pts are XX points // . there is no duplicate in each list separately // for ( i = 0 ; i < n2 ; ++i ) { found = false; if ( *Ds2x_pts[i] == incumbent ) found = true; else { for ( j = 0 ; j < n1 ; ++j ) if ( *Ds2x_pts[i] == *oracle_pts[j] ) { found = true; break; } } if ( !found ) l2.push_back ( Ds2x_pts[i] ); } n2 = l2.size(); for ( i = 0 ; i < n3 ; ++i ) { found = false; if ( *improv_pts[i] == incumbent ) found = true; else { for ( j = 0 ; j < n1 ; ++j ) if ( *improv_pts[i] == *oracle_pts[j] ) { found = true; break; } } if ( !found ) { for ( j = 0 ; j < n2 ; ++j ) if ( improv_pts[i] == l2[j] ) { found = true; break; } } if ( !found ) l3.push_back ( improv_pts[i] ); } n3 = l3.size(); // 2. construct the list of trial points: // ------------------------------------ trial_pts.clear(); int nb_pts = static_cast ( n1 + n2 + n3 ); // no need to reduce the number of trial points: if ( max_pts <= 0 || nb_pts <= max_pts ) { // 1. oracle points: for ( i = 0 ; i < n1 ; ++i ) trial_pts.push_back ( oracle_pts[i] ); // 2, improv points: for ( i = 0 ; i < n3 ; ++i ) trial_pts.push_back ( l3[i] ); // 3. Ds2x points: for ( i = 0 ; i < n2 ; ++i ) trial_pts.push_back ( l2[i] ); } // reduce the list to max_pts points: else { nb_pts = 0; size_t i1 = 0 , i2 = 0 , i3 = 0; while ( true ) { // one point from the oracle points: if ( i1 < n1 ) { trial_pts.push_back ( oracle_pts[i1++] ); ++nb_pts; if ( nb_pts == max_pts ) break; } // two from the improv points: for ( i = 0 ; i < 2 ; ++i ) { if ( i3 < n3 ) { trial_pts.push_back ( l3[i3++] ); ++nb_pts; if ( nb_pts == max_pts ) break; } } if ( nb_pts == max_pts ) break; // one from the Ds2x points: if ( i2 < n2 ) { trial_pts.push_back ( l2[i2++] ); ++nb_pts; if ( nb_pts == max_pts ) break; } } } // 3. display the list of trial points: // ------------------------------------ // if ( display_degree == NOMAD::FULL_DISPLAY ) { // out.open_block ( "list of trial points (debug)" ); // n1 = trial_pts.size(); // for ( i = 0 ; i < n1 ; ++i ) { // out << "#"; // out.display_int_w ( i , n1 ); // out << " x=( " << *trial_pts[i] << " )" << std::endl; // } // out.close_block(); // } } /*---------------------------------------------------------*/ /* create the list of improv points (private) */ /*---------------------------------------------------------*/ void NOMAD::TGP_Model_Search::create_improv_pts ( const std::vector & XX , // IN const NOMAD::Point & incumbent , // IN int max_pts , // IN const NOMAD::Display & out , // IN NOMAD::dd_type display_degree , // IN int display_lim , // IN std::vector & improv_pts ) const // OUT { if ( display_degree == NOMAD::FULL_DISPLAY ) out << NOMAD::open_block ( "improv points construction" ) << std::endl; // reset improv points: improv_pts.clear(); // display improv points directly from the NOMAD::TGP_Model object: #ifdef TGP_DEBUG out << NOMAD::open_block ( "expected improvement of the objective (improv)" ); _model->display_improv ( out , display_lim ); out << NOMAD::close_block() << std::endl; #endif std::list pts_indexes; _model->get_improv_points ( pts_indexes ); if ( pts_indexes.empty() ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << NOMAD::close_block ( "no improv candidate" ) << std::endl; return; } std::list::const_iterator it , end = pts_indexes.end(); // with constraints, the list is re-sorted in order to put the // predicred feasible points first: if ( _p.has_constraints() ) { std::list feas_pts , infeas_pts; NOMAD::Double h , f; const NOMAD::Double & h_min = _p.get_h_min(); for ( it = pts_indexes.begin() ; it != end ; ++it ) { if ( predict ( *XX[*it] , h , f ) && h <= h_min ) feas_pts.push_back ( *it ); else infeas_pts.push_back ( *it ); } pts_indexes.clear(); end = feas_pts.end(); for ( it = feas_pts.begin() ; it != end ; ++it ) pts_indexes.push_back ( *it ); end = infeas_pts.end(); for ( it = infeas_pts.begin() ; it != end ; ++it ) pts_indexes.push_back ( *it ); end = pts_indexes.end(); } // compute max_index just for the display: int i , j , max_index = -1 , ni = -1; if ( display_degree == NOMAD::FULL_DISPLAY ) { ni = pts_indexes.size(); if ( max_pts > 0 && ni > max_pts ) ni = max_pts; for ( it = pts_indexes.begin() ; it != end ; ++it ) { if ( *it > max_index ) max_index = *it; } } // add the points to improv_pts: bool rejected; i = j = 0; for ( it = pts_indexes.begin() ; it != end ; ++it ) { // we check the max number of points: rejected = ( max_pts > 0 && max_pts == i ); // we reject the point if it is the incumbent: if ( !rejected && incumbent == *XX[*it] ) rejected = true; // we add the point: if ( !rejected ) { improv_pts.push_back ( XX[*it] ); ++i; } // display: if ( display_degree == NOMAD::FULL_DISPLAY ) { if ( display_lim <= 0 || j < display_lim ) { if ( rejected ) out << "rejected candidate "; else { out << "improv candidate #"; out.display_int_w ( i-1 , ni ); } out << " (XX point #"; out.display_int_w ( *it , max_index ); out << "): x=( "; XX[*it]->NOMAD::Point::display ( out , " " , 6 , -1 ); out << " )"; if ( rejected ) { if ( max_pts > 0 && max_pts == i ) out << " (max number of points)"; else out << " (candidate==incumbent)"; } else out << " improv=" << _model->get_improv(*it); out << std::endl; } if ( display_lim > 0 && j == display_lim ) out << "..." << std::endl; ++j; } // if no display, stop the loop if there is already too many points: else if ( max_pts > 0 && max_pts == i ) break; } if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << NOMAD::close_block ( "end of improv points construction" ) << std::endl; } /*------------------------------------------------------------*/ /* create the list of Ds2x points, the points that maximize */ /* the expected reduction in predictive variance */ /* (private) */ /*------------------------------------------------------------*/ void NOMAD::TGP_Model_Search::create_Ds2x_pts ( const std::vector & XX , // IN const NOMAD::Display & out , // IN NOMAD::dd_type display_degree , // IN int display_lim , // IN std::vector & Ds2x_pts ) const // OUT { if ( display_degree == NOMAD::FULL_DISPLAY ) out << NOMAD::open_block ( "Ds2x points construction" ) << std::endl; // reset Ds2x points: Ds2x_pts.clear(); // display Ds2x points directly from the NOMAD::TGP_Model object: #ifdef TGP_DEBUG out << NOMAD::open_block ( "expected reduction in predictive variance (Ds2x)" ); _model->display_Ds2x ( out , display_lim ); out << NOMAD::close_block() << std::endl; #endif std::set pts_indexes; _model->get_Ds2x_points ( pts_indexes ); if ( pts_indexes.empty() ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << NOMAD::close_block ( "no Ds2x candidate" ) << std::endl; return; } std::set::const_iterator it , end = pts_indexes.end(); int i , max_index = -1 , m = _p.get_bb_nb_outputs() , n_XX = XX.size(); if ( display_degree == NOMAD::FULL_DISPLAY ) { for ( it = pts_indexes.begin() ; it != end ; ++it ) { if ( *it > max_index ) max_index = *it; } } for ( it = pts_indexes.begin() , i = 0 ; it != end ; ++it , ++i ) { Ds2x_pts.push_back ( XX[*it] ); if ( display_degree == NOMAD::FULL_DISPLAY ) { out << "Ds2x candidate #"; out.display_int_w ( i , m ); out << " (XX point #"; out.display_int_w ( *it , max_index ); out << "): x=( "; XX[*it]->NOMAD::Point::display ( out , " " , 6 , -1 ); out << " )"; if ( *it == n_XX - 1 ) out << " (rejected: candidate==incumbent)"; out << std::endl; } } if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << NOMAD::close_block ( "end of Ds2x points construction" ) << std::endl; } /*-------------------------------------------------------------------*/ /* create a list of oracle points, given by the model optimization */ /* (private) */ /*-------------------------------------------------------------------*/ bool NOMAD::TGP_Model_Search::create_oracle_pts ( const NOMAD::Cache & cache , const NOMAD::Point & incumbent , const NOMAD::Point & delta_m , const NOMAD::Display & out , NOMAD::dd_type display_degree , int display_lim , const std::vector & XX , std::vector & oracle_pts , bool & stop , NOMAD::stop_type & stop_reason ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << NOMAD::open_block ( "oracle points construction" ) << std::endl; // reset oracle points: NOMAD::TGP_Model_Search::clear_pts ( oracle_pts ); int i , n_XX = XX.size(); // starting points selection: const NOMAD::Eval_Point * x0s[3]; x0s[0] = x0s[1] = x0s[2] = NULL; // open display block for model predictions: #ifdef TGP_DEBUG out << NOMAD::open_block ( "TGP predictions (XX+ZZ)"); int i0 = ( display_lim > 0 ) ? n_XX - display_lim : 0; if ( i0 > 0 ) out << "..." << std::endl; #endif NOMAD::Double f_model , h_model; const NOMAD::Double & h_min = _p.get_h_min(); NOMAD::hnorm_type h_norm = _p.get_h_norm(); for ( i = 0 ; i < n_XX ; ++i ) { // compute model h and f values: _model->eval_hf ( XX[i]->get_bb_outputs() , h_min , h_norm , h_model , f_model ); if ( h_model.is_defined() && f_model.is_defined() ) { XX[i]->set_f ( f_model ); XX[i]->set_h ( h_model ); // feasible point: if ( XX[i]->is_feasible ( h_min ) ) { if ( !x0s[0] || f_model < x0s[0]->get_f() ) x0s[0] = XX[i]; } // infeasible point: else { if ( !x0s[1] || h_model < x0s[1]->get_h() ) x0s[1] = XX[i]; if ( !x0s[2] || f_model < x0s[2]->get_f() ) x0s[2] = XX[i]; } } // display model predictions: #ifdef TGP_DEBUG if ( i >= i0 ) { out << "#"; out.display_int_w ( i , n_XX ); out << " x=("; XX[i]->NOMAD::Point::display ( out , " " , 15 , -1 ); out << " ) m(x)=["; XX[i]->get_bb_outputs().display ( out , " " , 15 , -1 ); out << " ]"; if ( h_model.is_defined() && f_model.is_defined() ) out << " hm=" << h_model << " fm=" << f_model; else out << " no model value"; out << std::endl; } #endif #ifdef MODEL_STATS if ( XX[i] && f_model.is_defined() && h_model.is_defined() ) { XX[i]->set_mod_use ( 1 ); // 1 for model search XX[i]->set_Yw ( _model->get_Yw () ); XX[i]->set_nY ( p ); XX[i]->set_mh ( h_model ); XX[i]->set_mf ( f_model ); } #endif } #ifdef TGP_DEBUG // close display block for model predictions: { std::ostringstream oss; oss << "(size=" << n_XX << ")"; out << NOMAD::close_block ( oss.str() ) << std::endl; } // compute and display prediction errors: out << NOMAD::open_block ( "prediction relative errors on X(%)" ); _model->display_X_errors ( out ); out << NOMAD::close_block() << std::endl; #endif if ( !x0s[0] && !x0s[1] && !x0s[2] ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << NOMAD::close_block ( "oracle points error: no model starting point" ) << std::endl; return false; } // display starting points: if ( display_degree == NOMAD::FULL_DISPLAY ) { out << std::endl << NOMAD::open_block ( "model starting points" ); for ( i = 0 ; i < 3 ; ++i ) { out << "#" << i << ": "; if ( !x0s[i] ) out << "NULL" << std::endl; else { out << " x=("; x0s[i]->NOMAD::Point::display ( out , " " , 15 , -1 ); out << " ) m(x)=["; x0s[i]->get_bb_outputs().display ( out , " " , 15 , -1 ); out << " ]" << " hm=" << std::setw(15) << x0s[i]->get_h() << " fm=" << std::setw(15) << x0s[i]->get_f() << std::endl; } } out << NOMAD::close_block() << std::endl; } // optimize model: // --------------- NOMAD::Point * xf = NULL , * xi = NULL; NOMAD::Clock clock; bool optimization_ok = optimize_model ( x0s , out , display_degree , xf , xi , stop , stop_reason ); _one_search_stats.add_optimization_time ( clock.get_CPU_time() ); if ( stop || !optimization_ok || ( xf == NULL && xi == NULL ) ) { std::string error_str; if ( xf == NULL && xi == NULL ) error_str = "no model optimization solution"; else { if ( xf ) delete xf; if ( xi ) delete xi; error_str = ( stop ) ? "algorithm stop" : "model optimization error"; } if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << NOMAD::close_block ( "oracle points error: " + error_str ) << std::endl; return false; } // project and check xf and xi: if ( xf && !check_oracle_point ( cache , incumbent , delta_m , out , display_degree , *xf ) ) { delete xf; xf = NULL; } if ( xi && !check_oracle_point ( cache , incumbent , delta_m , out , display_degree , *xi ) ) { delete xi; xi = NULL; } // add xf and xi in the list of oracle points: if ( xf ) oracle_pts.push_back ( xf ); if ( xi ) { // check that xi != xf: if ( xf && *xf == *xi ) { delete xi; xi = NULL; } if ( xi ) oracle_pts.push_back ( xi ); } if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << NOMAD::close_block ( "end of oracle points construction" ) << std::endl; return true; } /*------------------------------------------------------*/ /* project and accept or reject an oracle trial point */ /* (private) */ /*------------------------------------------------------*/ bool NOMAD::TGP_Model_Search::check_oracle_point ( const NOMAD::Cache & cache , const NOMAD::Point & incumbent , const NOMAD::Point & delta_m , const NOMAD::Display & out , NOMAD::dd_type display_degree , NOMAD::Point & x ) { bool proj_to_mesh = _p.get_model_search_proj_to_mesh(); if ( display_degree == NOMAD::FULL_DISPLAY ) { out << std::endl << "oracle candidate"; if ( proj_to_mesh ) out << " (before projection)"; out << ": ( " << x << " )" << std::endl; } // projection to mesh: if ( proj_to_mesh ) { x.project_to_mesh ( incumbent , delta_m , _p.get_lb() , _p.get_ub() ); if ( display_degree == NOMAD::FULL_DISPLAY ) out << "oracle candidate (after projection) : ( " << x << " )" << std::endl; } // compare x and incumbent coordinates: if ( x == incumbent ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << "oracle candidate rejected (candidate==incumbent)" << std::endl; return false; } // two evaluations points are created in order to: // 1. check if the candidate is in cache // 2. have a prediction at x and at the incumbent: int n = x.size() , m = _p.get_bb_nb_outputs(); NOMAD::Eval_Point * tk = new NOMAD::Eval_Point ( n , m ); // trial point tk->Point::operator = ( x ); // check if the point is in cache: if ( cache.find ( *tk ) ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << "oracle candidate rejected (found in cache)" << std::endl; delete tk; return false; } NOMAD::Eval_Point * ic = new NOMAD::Eval_Point ( n , m ); // incumbent copy ic->Point::operator = ( incumbent ); // model predictions (in order to accept or reject the trial point): bool pred_error = !_model->predict ( *ic , true ) || // pred_outside_bnds = true !_model->predict ( *tk , true ); // pred_outside_bnds = true const NOMAD::Double & h_min = _p.get_h_min(); NOMAD::hnorm_type h_norm = _p.get_h_norm(); NOMAD::Double h0 , f0; // model values of f and h at the center NOMAD::Double h1 , f1; // model values of f and h at the trial point if ( !pred_error ) _model->eval_hf ( tk->get_bb_outputs() , h_min , h_norm , h1 , f1 ); delete tk; if ( pred_error || !h1.is_defined() || !f1.is_defined() ) { if ( display_degree == NOMAD::FULL_DISPLAY ) { if ( pred_error ) out << "prediction error: oracle candidate rejected"; else out << "no model value (EB constraint violated?): oracle candidate rejected"; out << std::endl; } delete ic; return false; } // accept or reject the trial point: bool accept_point = false; _model->eval_hf ( ic->get_bb_outputs(), h_min, h_norm, h0, f0 ); if ( !h0.is_defined() || !f0.is_defined() ) accept_point = true; delete ic; if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << "incumbent prediction : h0=" << h0 << " f0=" << f0 << std::endl << "trial point prediction: h1=" << h1 << " f1=" << f1 << std::endl; if ( !accept_point ) accept_point = (f1 < f0) || (h1 < h0); if ( !accept_point ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << "oracle candidate rejected" << std::endl; _one_search_stats.add_MS_rejected(); return false; } if ( display_degree == NOMAD::FULL_DISPLAY ) out << "oracle candidate accepted" << std::endl; return true; } /*--------------------------------------------------------*/ /* insert a trial point in the evaluator control object */ /* (private) */ /*--------------------------------------------------------*/ void NOMAD::TGP_Model_Search::register_point ( NOMAD::Point x , NOMAD::Signature & signature , const NOMAD::Point & incumbent , int mesh_index , NOMAD::dd_type display_degree , NOMAD::Evaluator_Control & ev_control ) const { int n = x.size(); NOMAD::Eval_Point * tk = new NOMAD::Eval_Point ( n , _p.get_bb_nb_outputs() ); // if the search is optimistic, a direction is computed (this // will be used in case of success in the speculative search): if ( _p.get_model_search_optimistic() ) { NOMAD::Direction dir ( n , 0.0 , NOMAD::MODEL_SEARCH_DIR ); dir.Point::operator = ( x - incumbent ); tk->set_direction ( &dir ); } tk->set_signature ( &signature ); tk->set_mesh_index ( &mesh_index ); tk->Point::operator = ( x ); // add the new point to the list of search trial points: ev_control.add_eval_point ( tk , display_degree , _p.get_snap_to_bounds() , NOMAD::Double() , NOMAD::Double() , NOMAD::Double() , NOMAD::Double() ); #ifdef MODEL_STATS if ( tk ) { NOMAD::Double h1 , f1; if ( _model->predict ( *tk , true ) ) // pred_outside_bnds = true _model->eval_hf ( tk->get_bb_outputs() , _p.get_h_min() , _p.get_h_norm() , h1 , f1 ); if ( h1.is_defined() && f1.is_defined() ) { tk->set_mod_use ( 1 ); // 1 for model search tk->set_Yw ( _model->get_Yw() ); tk->set_nY ( model.get_p () ); tk->set_mh ( h1 ); tk->set_mf ( f1 ); } } #endif } /*---------------------------------------------------------------*/ /* model construction (private) */ /*---------------------------------------------------------------*/ bool NOMAD::TGP_Model_Search::model_construction ( const NOMAD::Cache & cache , const NOMAD::Point & incumbent , const NOMAD::Point & delta_m , const NOMAD::Display & out , NOMAD::dd_type display_degree , int display_lim , NOMAD::Stats & stats , bool & compute_Ds2x , std::vector & XX , bool & stop , NOMAD::stop_type & stop_reason , std::string & error_str ) { int i , n_XX , n = incumbent.size(); compute_Ds2x = false; // reset XX: { n_XX = XX.size(); for ( i = 0 ; i < n_XX ; ++i ) delete XX[i]; XX.clear(); n_XX = 0; } error_str.clear(); if ( stop ) return false; NOMAD::Clock clock; // TGP model creation: if ( _model ) delete _model; NOMAD::TGP_mode_type tgp_mode = _p.get_model_tgp_mode(); _model = new NOMAD::TGP_Model ( n , _p.get_bb_output_type() , out , tgp_mode ); // construct interpolation set (X): // -------------------------------- if ( !_model->set_X ( cache , &incumbent , _p.get_seed() , true ) ) { // remove_fv = true if ( _model->get_nep_flag() ) _one_search_stats.add_not_enough_pts(); stats.update_model_stats ( _one_search_stats ); _all_searches_stats.update ( _one_search_stats ); error_str = _model->get_error_str(); delete _model; _model = NULL; return false; } // create the list of prediction points (XX) // (they are used to get starting points // and to get IMPROV and DS2X stats): // ----------------------------------- set_XX ( cache , n , _p.get_bb_output_type().size() , incumbent , delta_m , XX ); n_XX = XX.size(); // display sets X and XX: // ---------------------- #ifdef TGP_DEBUG // X: _model->display_X ( out , display_lim ); // XX (only the 10 last points are displayed): out << NOMAD::open_block ( "prediction points (XX)"); int i0 = ( display_lim > 0 ) ? n_XX - display_lim : 0; if ( i0 > 0 ) out << "..." << std::endl; else if ( i0 < 0 ) i0 = 0; for ( i = i0 ; i < n_XX ; ++i ) { out << "#"; out.display_int_w ( i , n_XX ); out << " x=("; XX[i]->NOMAD::Point::display ( out , " " , 15 , -1 ); out << " )" << std::endl; } std::ostringstream oss; oss << "(size=" << n_XX << ")"; out << NOMAD::close_block ( oss.str() ) << std::endl; #endif // model construction: // ------------------- int p = _model->get_p(); // number of points in X if ( display_degree == NOMAD::FULL_DISPLAY ) { out << "TGP model construction (p=" << p << ") ..."; out.flush(); } // decide compute_Ds2x flag: compute_Ds2x = true; if ( tgp_mode == NOMAD::TGP_PRECISE && n_XX > 100 ) compute_Ds2x = false; if ( !_model->compute ( XX , compute_Ds2x , true , // compute_improv = true false ) ) { // pred_outside_bnds = false _one_search_stats.add_construction_error(); if ( display_degree == NOMAD::FULL_DISPLAY ) out << "... error" << std::endl; // delete XX points: for ( i = 0 ; i < n_XX ; ++i ) delete XX[i]; XX.clear(); n_XX = 0; stats.update_model_stats ( _one_search_stats ); _all_searches_stats.update ( _one_search_stats ); error_str = _model->get_error_str(); delete _model; _model = NULL; // check if ctrl-c has been pressed: if ( NOMAD::TGP_Output_Model::get_force_quit() ) { stop = true; stop_reason = NOMAD::CTRL_C; } return false; } if ( display_degree == NOMAD::FULL_DISPLAY ) out << "... OK" << std::endl << std::endl; // update model stats: _one_search_stats.add_construction_time ( clock.get_CPU_time() ); _one_search_stats.update_nY ( p ); _one_search_stats.add_nb_truth(); _one_search_stats.add_nb_TGP(); return true; } /*---------------------------------------------------------------*/ /* optimize a model (private) */ /*---------------------------------------------------------------*/ bool NOMAD::TGP_Model_Search::optimize_model ( const NOMAD::Eval_Point * x0s[3] , const NOMAD::Display & out , NOMAD::dd_type display_degree , NOMAD::Point *& xf , NOMAD::Point *& xi , bool & stop , NOMAD::stop_type & stop_reason ) { // reset xf and xi: if ( xf ) delete xf; xf = NULL; if ( xi ) delete xi; xi = NULL; if ( !_model ) { if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << "model optimization error (no model)" << std::endl; return false; } std::string error_str; bool error = false; int i , n = _model->get_n0(); // model bounds: const NOMAD::Point & lb = _model->get_lb(); const NOMAD::Point & ub = _model->get_ub(); // initial displays: if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << NOMAD::open_block ( "model optimization" ); // parameters creation: NOMAD::Parameters model_param ( out ); // random seed: model_param.set_SEED ( _p.get_seed() + 10 * _all_searches_stats.get_MS_nb_searches() ); // number of variables: model_param.set_DIMENSION ( n ); // blackbox outputs: model_param.set_BB_OUTPUT_TYPE ( _p.get_bb_output_type() ); // blackbox inputs: model_param.set_BB_INPUT_TYPE ( _p.get_bb_input_type() ); // barrier parameters: model_param.set_H_MIN ( _p.get_h_min () ); model_param.set_H_NORM ( _p.get_h_norm() ); // starting points: for ( i = 0 ; i < 3 ; ++i ) if ( x0s[i] ) model_param.set_X0 ( *x0s[i] ); // fixed variables: for ( i = 0 ; i < n ; ++i ) if ( lb[i] == ub[i] || _p.variable_is_fixed(i) ) model_param.set_FIXED_VARIABLE(i); // no model search and no model ordering: model_param.set_MODEL_SEARCH ( false ); model_param.set_MODEL_EVAL_SORT ( false ); // display: model_param.set_DISPLAY_DEGREE ( NOMAD::NO_DISPLAY ); if ( display_degree == NOMAD::FULL_DISPLAY ) { model_param.set_DISPLAY_DEGREE ( NOMAD::NORMAL_DISPLAY ); // model_param.set_DISPLAY_DEGREE ( NOMAD::FULL_DISPLAY ); if ( n <= 5 ) model_param.set_DISPLAY_STATS ( "bbe mesh_index ( %14.12gsol ) %14.12gobj" ); else if ( n <= 10 ) model_param.set_DISPLAY_STATS ( "bbe mesh_index ( sol ) obj" ); else model_param.set_DISPLAY_STATS ( "bbe obj" ); } // mesh: int mesh_index = NOMAD::Mesh::get_mesh_index(); int min_mesh_index = NOMAD::Mesh::get_min_mesh_index(); int max_mesh_index = NOMAD::Mesh::get_max_mesh_index(); int max_halton_index = NOMAD::Mesh::get_max_halton_index(); NOMAD::Mesh::init ( 4.0 , 1 , -1 , 0 ); // searches: // model_param.set_LH_SEARCH ( 1000 , 100 ); // model_param.set_OPPORTUNISTIC_LH ( true ); // model_param.set_VNS_SEARCH ( true ); // maximum number of evaluations (2000 or 10000): model_param.set_MAX_BB_EVAL ( ( _p.get_model_tgp_mode() == NOMAD::TGP_PRECISE ) ? 10000 : 2000 ); // min mesh size: // model_param.set_MAX_MESH_INDEX ( 30 ); // model_param.set_MIN_MESH_SIZE ( NOMAD::Double ( 1e-8 ) , false ); model_param.set_SNAP_TO_BOUNDS ( true ); // model_param.set_SNAP_TO_BOUNDS ( false ); // disable user calls: model_param.set_USER_CALLS_ENABLED ( false ); // set flags: bool flag_check_bimads , flag_reset_mesh , flag_reset_barriers , flag_p1_active; NOMAD::Mads::get_flags ( flag_check_bimads , flag_reset_mesh , flag_reset_barriers , flag_p1_active ); NOMAD::Mads::set_flag_check_bimads ( true ); NOMAD::Mads::set_flag_reset_mesh ( true ); NOMAD::Mads::set_flag_reset_barriers ( true ); NOMAD::Mads::set_flag_p1_active ( false ); // bounds: model_param.set_LOWER_BOUND ( lb ); model_param.set_UPPER_BOUND ( ub ); try { // parameters validation: model_param.check(); // out << "TGP PARAMETERS:" << std::endl << model_param << std::endl; // model evaluator creation: NOMAD::TGP_Model_Evaluator ev ( model_param , *_model ); // algorithm creation and execution: NOMAD::Mads mads ( model_param , &ev ); NOMAD::stop_type st = mads.run(); // check the stopping criterion: if ( st == NOMAD::CTRL_C || st == NOMAD::MAX_CACHE_MEMORY_REACHED ) { std::ostringstream oss; oss << "model optimization: " << st; error_str = oss.str(); error = true; stop = true; stop_reason = st; } else if ( st == NOMAD::MAX_BB_EVAL_REACHED ) _one_search_stats.add_MS_max_bbe(); // update the stats on the number of model evaluations: _one_search_stats.update_MS_model_opt ( mads.get_stats().get_bb_eval() ); // get the solution(s): const NOMAD::Eval_Point * best_feas = mads.get_best_feasible (); const NOMAD::Eval_Point * best_infeas = mads.get_best_infeasible(); if ( best_feas ) xf = new NOMAD::Point ( *best_feas ); if ( best_infeas ) xi = new NOMAD::Point ( *best_infeas ); if ( !xf && !xi ) { error = true; error_str = "optimization error: no solution"; } } catch ( std::exception & e ) { error = true; error_str = std::string ( "optimization error: " ) + e.what(); } // reset flags: NOMAD::Mads::set_flag_check_bimads ( flag_check_bimads ); NOMAD::Mads::set_flag_reset_mesh ( flag_reset_mesh ); NOMAD::Mads::set_flag_reset_barriers ( flag_reset_barriers ); NOMAD::Mads::set_flag_p1_active ( flag_p1_active ); // reset mesh to what it was before: NOMAD::Mesh::init ( _p.get_mesh_update_basis().value() , _p.get_mesh_coarsening_exponent() , _p.get_mesh_refining_exponent() , _p.get_initial_mesh_index() ); NOMAD::Mesh::set_max_halton_index ( max_halton_index ); NOMAD::Mesh::set_mesh_index ( min_mesh_index ); NOMAD::Mesh::set_mesh_index ( max_mesh_index ); NOMAD::Mesh::set_mesh_index ( mesh_index ); // close display block: if ( display_degree == NOMAD::FULL_DISPLAY ) { if ( error ) out.close_block ( error_str ); else out.close_block(); } return !error; } /*---------------------------------------------------------*/ /* display the prediction error for the evaluated points */ /* (private) */ /*---------------------------------------------------------*/ bool NOMAD::TGP_Model_Search::predict ( const NOMAD::Point & x , NOMAD::Double & h , NOMAD::Double & f ) const { h.clear(); f.clear(); if ( !_model ) return false; NOMAD::Eval_Point y ( x.size() , _p.get_bb_nb_outputs() ); y.NOMAD::Point::operator = ( x ); if ( _model->predict ( y , true ) ) // pred_outside_bnds = true _model->eval_hf ( y.get_bb_outputs() , _p.get_h_min() , _p.get_h_norm() , h , f ); return ( h.is_defined() && f.is_defined() ); } /*---------------------------------------------------------*/ /* display the prediction error for the evaluated points */ /* (private) */ /*---------------------------------------------------------*/ void NOMAD::TGP_Model_Search::display_eval_pred_errors ( const std::list & evaluated_pts , const NOMAD::Display & out ) { if ( !_model ) return; int i , j = 0; int nb_pts = evaluated_pts.size() , n = _model->get_n0() , m = _p.get_bb_nb_outputs(); const NOMAD::Double & h_min = _p.get_h_min(); NOMAD::hnorm_type h_norm = _p.get_h_norm(); NOMAD::Double h , f , hm , fm , err; NOMAD::Eval_Point x ( n , m ); out << std::endl << NOMAD::open_block ( "evaluated points" ); std::list::const_iterator it , end = evaluated_pts.end(); for ( it = evaluated_pts.begin() ; it != end ; ++it ) { if ( !(*it) ) continue; h = (*it)->get_h(); f = (*it)->get_f(); for ( i = 0 ; i < n ; ++i ) x[i] = (**it)[i]; _model->predict ( x , true ); // pred_outside_bnds = true _model->eval_hf ( x.get_bb_outputs(), h_min , h_norm , hm , fm ); out << "#"; out.display_int_w ( j++ , nb_pts ); out << " x=("; if ( n < 5 ) (*it)->NOMAD::Point::display ( out , " " , 6 , -1 ); else (*it)->NOMAD::Point::display ( out , " " , -1 , 20 ); out << ")"; if ( _p.has_constraints() ) { err = (h.is_defined() && hm.is_defined()) ? h.rel_err(hm) * 100.0 : 100.0; out << " [h="; h.display ( out , "%11.3g" ); out << " hm="; hm.display ( out , "%11.3g" ); out << " err_h="; err.display ( out , "%.2f" ); out << "%]"; } err = (f.is_defined() && fm.is_defined()) ? f.rel_err(fm) * 100.0 : 100.0; out << " [f="; f.display ( out , "%11.3g" ); out << " fm="; fm.display ( out , "%11.3g" ); out << " err_f="; err.display ( out , "%.2f" ); out << "%]" << std::endl; } out.close_block(); } #endif