/*-------------------------------------------------------------------------------------*/ /* 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 Slave.cpp \brief Slave process (implementation) \author Sebastien Le Digabel \date 2010-04-22 \see Slave.hpp */ #include "Slave.hpp" /*-----------------------------------*/ /* static members initialization */ /*-----------------------------------*/ int NOMAD::Slave::_rank = -1; int NOMAD::Slave::_np = -1; int NOMAD::Slave::_data_sent = 0; int NOMAD::Slave::_data_rcvd = 0; bool NOMAD::Slave::_are_running = false; bool NOMAD::Slave::_stop_ok = false; /*----------------------------------------*/ /* initializations (private) */ /*----------------------------------------*/ void NOMAD::Slave::init ( void ) const { #ifdef USE_MPI MPI_Comm_rank ( MPI_COMM_WORLD, &NOMAD::Slave::_rank ); MPI_Comm_size ( MPI_COMM_WORLD, &NOMAD::Slave::_np ); #else NOMAD::Slave::_rank = 0; NOMAD::Slave::_np = 1; #endif // Slave::force_quit() will be called if ctrl-c is pressed: if ( !NOMAD::Slave::is_master() ) { NOMAD::Evaluator::force_quit(); signal ( SIGTERM , NOMAD::Slave::force_quit ); signal ( SIGINT , NOMAD::Slave::force_quit ); #ifndef WINDOWS signal ( SIGPIPE , NOMAD::Slave::force_quit ); // (ctrl-c during a "| more") #endif } } /*----------------------------------------*/ /* get the process rank */ /* (static) */ /*----------------------------------------*/ int NOMAD::Slave::get_rank ( void ) { if ( NOMAD::Slave::_rank < 0 ) { #ifdef USE_MPI MPI_Comm_rank ( MPI_COMM_WORLD, &NOMAD::Slave::_rank ); #else NOMAD::Slave::_rank = 0; #endif } return NOMAD::Slave::_rank; } /*----------------------------------------*/ /* get the number of processes */ /* (static) */ /*----------------------------------------*/ int NOMAD::Slave::get_nb_processes ( void ) { if ( NOMAD::Slave::_np < 0 ) { #ifdef USE_MPI MPI_Comm_size ( MPI_COMM_WORLD, &NOMAD::Slave::_np ); #else NOMAD::Slave::_np = 1; #endif } return NOMAD::Slave::_np; } /*----------------------*/ /* run the slave code */ /*----------------------*/ void NOMAD::Slave::run ( void ) const { #ifdef USE_MPI MPI_Request req; char signal = 0; NOMAD::Eval_Point * x = NULL; bool count_eval = false; while ( true ) { // receive signal from master: // --------------------------- NOMAD::Slave::receive_data ( &signal , 1 , MPI_CHAR , 0 , &req ); // slave is ready or not initialized: NOMAD::Slave::send_data ( &NOMAD::READY_SIGNAL , 1 , MPI_CHAR , 0 , false ); NOMAD::Slave::wait_request ( req ); // EVAL signal: // ------------ if ( signal == NOMAD::EVAL_SIGNAL ) { // receive and evaluate the point: x = eval_point ( count_eval ); } // RESULT signal: // -------------- else if ( signal == NOMAD::RESULT_SIGNAL ) { // send the evaluation result to the master: send_eval_result ( x , count_eval ); delete x; x = NULL; } // STOP signal: // ------------ else if ( signal == NOMAD::STOP_SIGNAL ) break; // WAIT signal: // ------------ // else if ( signal == NOMAD::WAIT_SIGNAL ) { // } } if ( x ) delete x; #endif } /*-----------------------------------------*/ /* initialize all the slaves */ /* (static) */ /*-----------------------------------------*/ void NOMAD::Slave::init_slaves ( const NOMAD::Display & out ) { #ifdef USE_MPI if ( !NOMAD::Slave::is_master() || NOMAD::Slave::_are_running ) return; NOMAD::dd_type display_degree = out.get_gen_dd(); if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << NOMAD::open_block ( "initializing slaves" ); MPI_Status status; MPI_Request ** req = new MPI_Request * [ NOMAD::Slave::_np ]; int nb_initialized = 0; int nb_slaves = NOMAD::Slave::_np - 1; int source; char signal; NOMAD::Clock clk; // 1. launch requests: for ( source = 1 ; source < NOMAD::Slave::_np ; ++source ) { req[source] = new MPI_Request; NOMAD::Slave::receive_data ( &signal , 1 , MPI_CHAR , source , req[source] ); if ( display_degree == NOMAD::FULL_DISPLAY ) out << "." << std::endl; } // 2. test requests (with a maximal delay of MAX_REQ_WAIT): int cnt = 0 , flag; while ( nb_initialized < nb_slaves && clk.get_real_time() < NOMAD::MAX_REQ_WAIT ) { for ( source = 1 ; source < NOMAD::Slave::_np ; ++source ) { if ( req[source] ) { MPI_Test ( req[source] , &flag , &status ); if ( flag ) { MPI_Wait ( req[source] , &status ); // send the WAIT signal: NOMAD::Slave::send_data ( &NOMAD::WAIT_SIGNAL , 1 , MPI_CHAR , source , true ); delete req[source]; req[source] = NULL; ++nb_initialized; } } } // a constant is used in order to display only a few '.' : if ( display_degree == NOMAD::FULL_DISPLAY && cnt%1000000==0 ) out << "." << std::endl; ++cnt; } // 3. delete requests: std::list err_list; for ( source = 1 ; source < NOMAD::Slave::_np ; ++source ) { if ( req[source] ) { err_list.push_back ( source ); MPI_Cancel ( req[source] ); delete req[source]; } } delete [] req; NOMAD::Slave::_are_running = true; NOMAD::Slave::_stop_ok = false; if ( display_degree == NOMAD::FULL_DISPLAY ) out << NOMAD::close_block() << std::endl; if ( !err_list.empty() ) { std::ostringstream oss; oss << "could not initialize slave"; if ( err_list.size() > 1 ) { oss << "s"; std::list::const_iterator it , end = err_list.end(); for ( it = err_list.begin() ; it != end ; ++it ) oss << " #" << *it; } else oss << " #" << *err_list.begin(); throw NOMAD::Exception ( "Slave.cpp" , __LINE__ , oss.str() ); } #endif } /*-----------------------------------------*/ /* stop the slaves */ /* (static) */ /*-----------------------------------------*/ void NOMAD::Slave::stop_slaves ( const NOMAD::Display & out ) { #ifdef USE_MPI if ( !NOMAD::Slave::is_master() || NOMAD::Slave::_stop_ok ) return; NOMAD::dd_type display_degree = out.get_gen_dd(); if ( display_degree == NOMAD::FULL_DISPLAY ) out << std::endl << NOMAD::open_block ( "stopping slaves" ); int nb_stopped = 0; int nb_slaves = NOMAD::Slave::_np - 1; int source; char signal; NOMAD::Clock clk; MPI_Status status; MPI_Request ** req = new MPI_Request * [ NOMAD::Slave::_np ]; // 1. launch requests: for ( source = 1 ; source < NOMAD::Slave::_np ; ++source ) { req[source] = new MPI_Request; NOMAD::Slave::receive_data ( &signal , 1 , MPI_CHAR , source , req[source] ); if ( display_degree == NOMAD::FULL_DISPLAY ) out << "." << std::endl; } // 2. test requests (with a maximal delay of MAX_REQ_WAIT): int cnt = 0 , flag; while ( nb_stopped < nb_slaves && clk.get_real_time() < NOMAD::MAX_REQ_WAIT ) { for ( source = 1 ; source < NOMAD::Slave::_np ; ++source ) { if ( req[source] ) { MPI_Test ( req[source] , &flag , &status ); if ( flag ) { MPI_Wait ( req[source] , &status ); // send the STOP signal: NOMAD::Slave::send_data ( &NOMAD::STOP_SIGNAL , 1 , MPI_CHAR , source , true ); delete req[source]; req[source] = NULL; ++nb_stopped; } } } // a constant is used in order to display only a few '.' : if ( display_degree == NOMAD::FULL_DISPLAY && cnt%1000000==0 ) out << "." << std::endl; ++cnt; } NOMAD::Slave::_are_running = false; NOMAD::Slave::_stop_ok = true; // 3. delete requests: for ( source = 1 ; source < NOMAD::Slave::_np ; ++source ) { if ( req[source] ) { MPI_Cancel ( req[source] ); delete req[source]; NOMAD::Slave::_stop_ok = false; } } delete [] req; if ( display_degree == NOMAD::FULL_DISPLAY ) out << NOMAD::close_block() << std::endl; #endif } #ifdef USE_MPI /*------------------------------------------------------*/ /* receive data (static, private) */ /*------------------------------------------------------*/ int NOMAD::Slave::receive_data ( void * buf , int count , MPI_Datatype datatype , int source , // may be MPI_ANY_SOURCE MPI_Request * req ) { int tag = ( NOMAD::Slave::is_master() ) ? source : NOMAD::Slave::get_rank(); // immediate receive: if ( req ) { if ( source == MPI_ANY_SOURCE ) throw NOMAD::Exception ( "Slave.cpp" , __LINE__ , "Slave::receive_data(): immediate receive with no source" ); MPI_Irecv ( buf , count , datatype , source , tag , MPI_COMM_WORLD , req ); } // normal receive: else { MPI_Status status; if ( source == MPI_ANY_SOURCE ) tag = MPI_ANY_TAG; MPI_Recv ( buf , count , datatype , source , tag , MPI_COMM_WORLD , &status ); source = status.MPI_SOURCE; } // stats: int size; MPI_Type_size ( datatype , &size ); NOMAD::Slave::_data_rcvd += count * size; return source; } /*------------------------------------------------------*/ /* send data (static, private) */ /*------------------------------------------------------*/ void NOMAD::Slave::send_data ( const void * buf , int count , MPI_Datatype datatype , int dest , bool ready_send ) { int tag = ( NOMAD::Slave::is_master() ) ? dest : NOMAD::Slave::get_rank(); // ready send: if ( ready_send ) MPI_Rsend ( const_cast(buf) , count , datatype , dest , tag , MPI_COMM_WORLD ); // normal send: else MPI_Send ( const_cast(buf) , count , datatype , dest , tag , MPI_COMM_WORLD ); // stats: int size; MPI_Type_size ( datatype , &size ); NOMAD::Slave::_data_sent += count * size; } /*------------------------------------------------------*/ /* receive and evaluate an Eval_Point from the master */ /* (private) */ /*------------------------------------------------------*/ NOMAD::Eval_Point * NOMAD::Slave::eval_point ( bool & count_eval ) const { // 1. receive the point: int itab[3]; MPI_Request req; NOMAD::Slave::receive_data ( itab , 3 , MPI_INT , 0 , &req ); NOMAD::Slave::send_data ( &NOMAD::READY_SIGNAL , 1 , MPI_CHAR , 0 , false ); NOMAD::Slave::wait_request ( req ); int n = itab[0]; double * dtab = new double[n+1]; NOMAD::Slave::receive_data ( dtab , n+1 , MPI_DOUBLE , 0 , &req ); NOMAD::Slave::send_data ( &NOMAD::READY_SIGNAL , 1 , MPI_CHAR , 0 , false ); NOMAD::Slave::wait_request ( req ); // 2. create the Eval_Point: NOMAD::Eval_Point * x = new NOMAD::Eval_Point ( n , _p->get_bb_nb_outputs() ); for ( int i = 0 ; i < n ; ++i ) (*x)[i] = dtab[i]; NOMAD::Double h_max = dtab[n]; x->set_tag ( itab[2] ); x->set_eval_type ( ( itab[1] > 0 ) ? NOMAD::SGTE : NOMAD::TRUTH ); delete [] dtab; // 3. evaluate the point: bool eval_ok; try { eval_ok = _ev->eval_x ( *x , h_max , count_eval ); } catch ( ... ) { eval_ok = false; } x->set_eval_status ( ( eval_ok ) ? NOMAD::EVAL_OK : NOMAD::EVAL_FAIL ); return x; } /*-----------------------------------------------------*/ /* send an evaluation result to the master (private) */ /*-----------------------------------------------------*/ void NOMAD::Slave::send_eval_result ( const NOMAD::Eval_Point * x , bool count_eval ) const { // receive a signal from the master: char signal; NOMAD::Slave::receive_data ( &signal , 1 , MPI_CHAR , 0 , NULL ); // send the evaluation result: int m = _p->get_bb_nb_outputs(); int s = 2*m+2; double * dtab = new double [s]; const NOMAD::Point & bbo = x->get_bb_outputs(); // bb_outputs (m values): for ( int i = 0 ; i < m ; ++i ) { if ( bbo[i].is_defined() ) { dtab[i ] = bbo[i].value(); dtab[i+m] = 1.0; } else { dtab[i ] = NOMAD::INF; dtab[i+m] = -1.0; } } // evaluation status: dtab[2*m] = ( x->get_eval_status() == NOMAD::EVAL_OK ) ? 1.0 : -1.0; // count_eval: dtab[s-1] = ( count_eval ) ? 1.0 : -1.0; // send the array: NOMAD::Slave::send_data ( dtab , s , MPI_DOUBLE , 0 , true ); delete [] dtab; } /*---------------------------------------------*/ /* receive an evaluation result from a slave */ /*---------------------------------------------*/ void NOMAD::Slave::receive_eval_result ( int slave_rank , NOMAD::Eval_Point * x , bool & eval_ok , bool & count_eval ) const { // send the RESULT signal to the slave: NOMAD::Slave::send_data ( &NOMAD::RESULT_SIGNAL , 1 , MPI_CHAR , slave_rank , true ); // receive the evaluation result as a double array: int m = _p->get_bb_nb_outputs(); int s = 2*m+2; double * dtab = new double [s]; MPI_Request req; NOMAD::Slave::receive_data ( dtab , s , MPI_DOUBLE , slave_rank , &req ); NOMAD::Slave::send_data ( &NOMAD::READY_SIGNAL , 1 , MPI_CHAR , slave_rank , false ); NOMAD::Slave::wait_request ( req ); // interpret the array: for ( int i = 0 ; i < m ; ++i ) x->set_bb_output ( i , ( dtab[i+m] > 0.0 ) ? dtab[i] : NOMAD::Double() ); eval_ok = ( dtab[2*m] > 0.0 ); x->set_eval_status ( eval_ok ? NOMAD::EVAL_OK : NOMAD::EVAL_FAIL ); count_eval = ( dtab[s-1] > 0.0 ); delete [] dtab; } /*-----------------------------------------------------*/ /* send an Eval_Point to a slave */ /*-----------------------------------------------------*/ void NOMAD::Slave::send_eval_point ( const NOMAD::Eval_Point * x , int slave_rank , const NOMAD::Double & h_max ) const { char signal; int itab[3]; int n = x->size(); // n: itab[0] = n; // evaluation type (+1: sgte eval; -1: true eval): itab[1] = ( x->get_eval_type() == NOMAD::SGTE ) ? 1 : -1; // tag of the point: itab[2] = x->get_tag(); // point coordinates: double * dtab = new double[n+1]; for ( int i = 0 ; i < n ; ++i ) dtab[i] = (*x)[i].value(); dtab[n] = h_max.value(); // wait for the slave signal: NOMAD::Slave::receive_data ( &signal , 1 , MPI_CHAR , slave_rank , NULL ); // send n and evaluation type: NOMAD::Slave::send_data ( itab , 3 , MPI_INT , slave_rank , true ); // wait for the slave signal: NOMAD::Slave::receive_data ( &signal , 1 , MPI_CHAR , slave_rank , NULL ); // send the point coordinates: NOMAD::Slave::send_data ( dtab , n+1 , MPI_DOUBLE , slave_rank , true ); delete [] dtab; } /*-----------------------------------------*/ /* wait for a MPI request */ /* (static) */ /*-----------------------------------------*/ void NOMAD::Slave::wait_request ( MPI_Request & req ) { MPI_Status status; MPI_Wait ( &req , &status ); } #endif