/*-------------------------------------------------------------------------------------*/ /* 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 Evaluator.cpp \brief Evaluation of blackbox functions (implementation) \author Sebastien Le Digabel \date 2010-04-14 \see Evaluator.hpp */ #include "Evaluator.hpp" /*-----------------------------------*/ /* static members initialization */ /*-----------------------------------*/ bool NOMAD::Evaluator::_force_quit = false; /*-----------------------------------------------------------------*/ /* constructor */ /*-----------------------------------------------------------------*/ /* Parameters::_bb_exe is used to construct _bb_exe and _bb_nbo: */ /* Parameters::_bb_exe can have similar blackbox names, while */ /* . _bb_exe will have unique blackbox names */ /* . _bb_exe includes the blackbox path */ /*-----------------------------------------------------------------*/ NOMAD::Evaluator::Evaluator ( const NOMAD::Parameters & p ) : _p ( p ) , _is_multi_obj ( false ) { NOMAD::Evaluator::_force_quit = false; if ( _p.get_bb_exe().empty() ) return; // _bbe_exe and _bb_nbo construction: std::list::const_iterator it = _p.get_bb_exe().begin(); _bb_exe.push_back(*it); _bb_nbo.push_back(1); ++it; std::list::const_iterator end = _p.get_bb_exe().end(); while ( it != end ) { if ( *it != _bb_exe[_bb_exe.size()-1] ) { _bb_exe.push_back(*it); _bb_nbo.push_back(1); } else ++_bb_nbo[_bb_exe.size()-1]; ++it; } // we check that _bb_exe contains unique names and we add the problem path: size_t k , l , n = _bb_exe.size() , nm1 = n-1; for ( k = 0 ; k < nm1 ; ++k ) { for ( l = k+1 ; l < n ; ++l ) if ( _bb_exe[k] == _bb_exe[l] ) throw NOMAD::Exception ( "Evaluator.cpp" , __LINE__ , "problem with executable names" ); } // construction of _sgte_exe: bool has_sgte_exe = _p.has_sgte_exe(); std::string err; if ( has_sgte_exe ) { for ( k = 0 ; k < n ; ++k ) { _sgte_exe.push_back ( _p.get_sgte_exe(_bb_exe[k]) ); if ( _sgte_exe[_sgte_exe.size()-1].empty() ) { err = "blackbox executable \'" + _bb_exe[k] + "\' has no surrogate"; throw NOMAD::Exception ( "Evaluator.cpp" , __LINE__ , err ); } } } // process blakc-box executables (check and add problem path): for ( k = 0 ; k < n ; ++k ) { process_bb_exe_name ( _bb_exe[k] ); if ( has_sgte_exe ) process_bb_exe_name ( _sgte_exe[k] ); } // blackbox names and indexes display: #ifdef DEBUG #ifdef USE_MPI int rank; MPI_Comm_rank ( MPI_COMM_WORLD, &rank); if ( rank == 0 ) { #else { #endif const NOMAD::Display & out = _p.out(); if ( !_bb_exe.empty() ) { out << std::endl << NOMAD::open_block ( "blackbox executables" ); for ( k = 0 ; k < n ; ++k ) { out << NOMAD::open_block ( "bb #" + NOMAD::itos(k) ) << _bb_exe[k] << std::endl << "number of outputs=" << _bb_nbo[k] << std::endl << NOMAD::close_block(); } out.close_block(); } if ( !_sgte_exe.empty() ) { out << std::endl << NOMAD::open_block ( "surrogate executables" ); for ( k = 0 ; k < n ; ++k ) out << "sgte #" << static_cast(k) << ": " << _sgte_exe[k] << std::endl; out.close_block(); } } #endif } /*----------------------------------------------------------------*/ /* process a blackbox executable name (private) */ /*----------------------------------------------------------------*/ void NOMAD::Evaluator::process_bb_exe_name ( std::string & bb_exe ) const { std::string err; std::list bb_exe_words; NOMAD::get_words ( bb_exe , bb_exe_words ); if ( bb_exe_words.empty() ) { err = "problem with executable \'" + bb_exe + "\'"; throw NOMAD::Exception ( "Evaluator.cpp" , __LINE__ , err ); } std::string problem_dir = _p.get_problem_dir(); // bb_exe is composed of several words (it is a command): if ( bb_exe_words.size() > 1 ) { bb_exe.clear(); std::list::const_iterator it = bb_exe_words.begin() , end = bb_exe_words.end(); while (true) { if ( (*it)[0] != '$' ) { bb_exe += "\"" + problem_dir; bb_exe += *it + "\""; } else bb_exe += it->substr ( 1 , it->size()-1 ); ++it; if ( it == end ) break; bb_exe += " "; } } // bb_exe is just composed of one name (it is an executable): else { if ( bb_exe[0] != '$' ) bb_exe = problem_dir + bb_exe; else bb_exe = bb_exe.substr ( 1 , bb_exe.size()-1 ); if ( !NOMAD::check_exe_file ( bb_exe ) ) { err = "\'" + bb_exe + "\' is not a valid executable file"; throw NOMAD::Exception ( "Evaluator.cpp" , __LINE__ , err ); } if ( bb_exe[0] != '$' ) bb_exe = "\"" + bb_exe + "\""; } } /*-----------------------------------------------------------------------*/ /* check the constraints to decide if an evaluation have to be stopped */ /*-----------------------------------------------------------------------*/ /* . checked when h > h_max or if a 'EB' constraint is violated */ /* . private method */ /*-----------------------------------------------------------------------*/ bool NOMAD::Evaluator::interrupt_evaluations ( const NOMAD::Eval_Point & x , const NOMAD::Double & h_max ) const { int nbo = _p.get_bb_nb_outputs(); const NOMAD::Point & bbo = x.get_bb_outputs(); const std::vector & bbot = _p.get_bb_output_type(); NOMAD::Double h = 0.0; bool check_h = h_max.is_defined(); for ( int i = 0 ; i < nbo ; ++i ) { if ( bbo[i].is_defined() && ( bbot[i] == NOMAD::EB || bbot[i] == NOMAD::PEB_E ) && bbo[i] > _p.get_h_min() ) return true; if ( check_h && bbo[i].is_defined() && (bbot[i] == NOMAD::FILTER || bbot[i] == NOMAD::PB || bbot[i] == NOMAD::PEB_P ) ) { if ( bbo[i] > _p.get_h_min() ) { switch ( _p.get_h_norm() ) { case NOMAD::L1: h += bbo[i]; break; case NOMAD::L2: h += bbo[i].pow2(); break; case NOMAD::LINF: if ( bbo[i] > h ) h = bbo[i]; break; } if ( _p.get_h_norm() == NOMAD::L2 ) { if ( h > h_max.pow2() ) return true; } else if ( h > h_max ) return true; } } } return false; } /*--------------------------------------------------------*/ /* compute f(x) from the blackbox outputs of a point */ /* (define a Multi_Obj_Evaluator to treat more than */ /* one objective) */ /*--------------------------------------------------------*/ void NOMAD::Evaluator::compute_f ( NOMAD::Eval_Point & x ) const { if ( x.get_bb_outputs().size() != _p.get_bb_nb_outputs() ) { std::ostringstream err; err << "Evaluator::compute_f(x): x has a wrong number of blackbox outputs (" << x.get_bb_outputs().size() << " != " << _p.get_bb_nb_outputs() << ")"; throw NOMAD::Exception ( "Evaluator.cpp" , __LINE__ , err.str() ); } x.set_f ( x.get_bb_outputs()[*(_p.get_index_obj().begin())] ); } /*--------------------------------------------------------*/ /* compute h(x) from the blackbox outputs of a point */ /* set also the flag 'EB_ok' of the point */ /*--------------------------------------------------------*/ void NOMAD::Evaluator::compute_h ( NOMAD::Eval_Point & x ) const { if ( x.get_bb_outputs().size() != _p.get_bb_nb_outputs() ) { std::ostringstream err; err << "Evaluator::compute_h(x): x has a wrong number of blackbox outputs (" << x.get_bb_outputs().size() << " != " << _p.get_bb_nb_outputs() << ")"; throw NOMAD::Exception ( "Evaluator.cpp" , __LINE__ , err.str() ); } int nbo = _p.get_bb_nb_outputs(); const std::vector & bbot = _p.get_bb_output_type(); const NOMAD::Point & bbo = x.get_bb_outputs(); NOMAD::Double h = 0.0 , bboi; x.set_EB_ok ( true ); for ( int i = 0 ; i < nbo ; ++i ) { bboi = bbo[i]; if ( bboi.is_defined() && (bbot[i] == NOMAD::EB || bbot[i] == NOMAD::PEB_E ) && bboi > _p.get_h_min() ) { h.clear(); x.set_h ( h ); x.set_EB_ok ( false ); return; } if ( bboi.is_defined() && ( bbot[i] == NOMAD::FILTER || bbot[i] == NOMAD::PB || bbot[i] == NOMAD::PEB_P ) ) { if ( bboi > _p.get_h_min() ) { switch ( _p.get_h_norm() ) { case NOMAD::L1: h += bboi; break; case NOMAD::L2: h += bboi * bboi; break; case NOMAD::LINF: if ( bboi > h ) h = bboi; break; } } } } if ( _p.get_h_norm() == NOMAD::L2 ) h = h.sqrt(); x.set_h ( h ); } /*-------------------------------------------------------------------*/ /* . evaluate the black boxes at a given Eval_Point */ /* . the function returns true if the evaluation did not fail */ /* . set count_eval=true to count the evaluation */ /* (unless the output value CNT_EVAL is defined and set to 1 */ /* by the blackbox) */ /*-------------------------------------------------------------------*/ bool NOMAD::Evaluator::eval_x ( NOMAD::Eval_Point & x , const NOMAD::Double & h_max , bool & count_eval ) const { count_eval = false; if ( _bb_exe.empty() || !x.is_complete() ) throw NOMAD::Exception ( "Evaluator.cpp" , __LINE__ , "Evaluator: no BB_EXE is defined (blackbox executable names)" ); bool sgte = x.get_eval_type() == NOMAD::SGTE; if ( sgte && _sgte_exe.empty() ) throw NOMAD::Exception ( "Evaluator.cpp" , __LINE__ , "Evaluator: no SGTE_EXE is defined (surrogate executable names)" ); int pid = NOMAD::get_pid(); int seed = _p.get_seed(); std::string tmp_dir = _p.get_tmp_dir(); std::ostringstream oss; oss << "." << seed; if ( pid != seed ) oss << "." << pid; oss << "." << x.get_tag() << "."; const std::string & sint = oss.str(); // for the parallel version: no need to include the process rank in the names // as the point tags are unique for all the processes: each process creates // its own points and uses Eval_Point::set_tag() // blackbox input file writing: // ---------------------------- std::string bb_input_file_name = tmp_dir + NOMAD::BLACKBOX_INPUT_FILE_PREFIX + sint + NOMAD::BLACKBOX_INPUT_FILE_EXT; std::string bb_output_file_name = tmp_dir + NOMAD::BLACKBOX_OUTPUT_FILE_PREFIX + sint + NOMAD::BLACKBOX_OUTPUT_FILE_EXT; std::ofstream fout ( bb_input_file_name.c_str() ); if ( fout.fail() ) { std::string err = "could not open file blackbox input file " + bb_input_file_name; throw NOMAD::Exception ( "Evaluator.cpp" , __LINE__ , err ); } // include seed: if ( _p.get_bb_input_include_seed() ) fout << seed << " "; // include tag: if ( _p.get_bb_input_include_tag() ) fout << x.get_tag() << " "; fout.setf ( std::ios::fixed ); fout.precision ( NOMAD::DISPLAY_PRECISION_BB ); x.Point::display ( fout , " " , -1 , -1 ); fout << std::endl; fout.close(); if ( fout.fail() ) return false; x.set_eval_status ( NOMAD::EVAL_IN_PROGRESS ); std::string cmd , bb_exe; std::ifstream fin; bool failed; NOMAD::Double d; int j , nbbok; int ibbo = 0; // system call to evaluate the blackbox: // ------------------------------------- size_t bn = _bb_exe.size(); for ( size_t k = 0 ; k < bn ; ++k ) { // executable name: bb_exe = ( sgte ) ? _sgte_exe[k] : _bb_exe[k]; // system command: cmd = bb_exe + " " + bb_input_file_name; // redirection ? if no, the blackbox has to create // the output file 'bb_output_file_name': if ( _p.get_bb_redirection() ) cmd += " > " + bb_output_file_name; #ifdef DEBUG #ifdef USE_MPI int rank; MPI_Comm_rank ( MPI_COMM_WORLD, &rank); _p.out() << "command(rank=" << rank << ") = \'" << cmd << "\'" << std::endl; #else _p.out() << "command=\'" << cmd << "\'" << std::endl; #endif #endif // the evaluation: { int signal = system ( cmd.c_str() ); // catch the ctrl-c signal: if ( signal == SIGINT ) raise ( SIGINT ); // other evaluation error: failed = ( signal != 0 ); count_eval = true; } // the evaluation failed (we stop the evaluations): if ( failed ) { x.set_eval_status ( NOMAD::EVAL_FAIL ); break; } // reading of the blackbox output file: // ------------------------------------ else { // bb-output file reading: fin.open ( bb_output_file_name.c_str() ); failed = false; bool is_defined = true; bool is_inf = false; // loop on the number of outputs for this blackbox: nbbok = _bb_nbo[k]; for ( j = 0 ; j < nbbok ; ++j ) { fin >> d; if ( !d.is_defined() ) { is_defined = false; break; } if ( fin.fail() ) { failed = true; break; } if ( d.value() >= NOMAD::INF ) { is_inf = true; break; } x.set_bb_output ( ibbo++ , d ); } fin.close(); // the evaluation failed: if ( failed || !is_defined || is_inf ) { x.set_eval_status ( NOMAD::EVAL_FAIL ); break; } // stop the evaluations if h > h_max or if a 'EB' constraint is violated: if ( k < _bb_exe.size() - 1 && interrupt_evaluations ( x , h_max ) ) break; } } if ( x.get_eval_status() == NOMAD::EVAL_IN_PROGRESS ) x.set_eval_status ( NOMAD::EVAL_OK ); // delete the blackbox input and output files: // ------------------------------------------- remove ( bb_input_file_name.c_str () ); remove ( bb_output_file_name.c_str() ); // check the CNT_EVAL output: // -------------------------- int index_cnt_eval = _p.get_index_cnt_eval(); if ( index_cnt_eval >= 0 && x.get_bb_outputs()[index_cnt_eval] == 0.0 ) count_eval = false; return x.is_eval_ok(); }