1 /*-------------------------------------------------------------------------------------*/
2 /*  NOMAD - Nonlinear Optimization by Mesh Adaptive Direct search - version 3.7.2      */
3 /*                                                                                     */
4 /*  Copyright (C) 2001-2015  Mark Abramson        - the Boeing Company, Seattle        */
5 /*                           Charles Audet        - Ecole Polytechnique, Montreal      */
6 /*                           Gilles Couture       - Ecole Polytechnique, Montreal      */
7 /*                           John Dennis          - Rice University, Houston           */
8 /*                           Sebastien Le Digabel - Ecole Polytechnique, Montreal      */
9 /*                           Christophe Tribes    - Ecole Polytechnique, Montreal      */
10 /*                                                                                     */
11 /*  funded in part by AFOSR and Exxon Mobil                                            */
12 /*                                                                                     */
13 /*  Author: Sebastien Le Digabel                                                       */
14 /*                                                                                     */
15 /*  Contact information:                                                               */
16 /*    Ecole Polytechnique de Montreal - GERAD                                          */
17 /*    C.P. 6079, Succ. Centre-ville, Montreal (Quebec) H3C 3A7 Canada                  */
18 /*    e-mail: nomad@gerad.ca                                                           */
19 /*    phone : 1-514-340-6053 #6928                                                     */
20 /*    fax   : 1-514-340-5665                                                           */
21 /*                                                                                     */
22 /*  This program is free software: you can redistribute it and/or modify it under the  */
23 /*  terms of the GNU Lesser General Public License as published by the Free Software   */
24 /*  Foundation, either version 3 of the License, or (at your option) any later         */
25 /*  version.                                                                           */
26 /*                                                                                     */
27 /*  This program is distributed in the hope that it will be useful, but WITHOUT ANY    */
28 /*  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A    */
29 /*  PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.   */
30 /*                                                                                     */
31 /*  You should have received a copy of the GNU Lesser General Public License along     */
32 /*  with this program. If not, see <http://www.gnu.org/licenses/>.                     */
33 /*                                                                                     */
34 /*  You can find information on the NOMAD software at www.gerad.ca/nomad               */
35 /*-------------------------------------------------------------------------------------*/
36 /**
37  \file   Slave.cpp
38  \brief  Slave process (implementation)
39  \author Sebastien Le Digabel
40  \date   2010-04-22
41  \see    Slave.hpp
42  */
43 #include "Slave.hpp"
44 
45 /*-----------------------------------*/
46 /*   static members initialization   */
47 /*-----------------------------------*/
48 int  NOMAD::Slave::_rank        = -1;
49 int  NOMAD::Slave::_np          = -1;
50 int  NOMAD::Slave::_data_sent   =  0;
51 int  NOMAD::Slave::_data_rcvd   =  0;
52 bool NOMAD::Slave::_are_running = false;
53 bool NOMAD::Slave::_stop_ok     = false;
54 
55 /*----------------------------------------*/
56 /*        initializations (private)       */
57 /*----------------------------------------*/
init(void) const58 void NOMAD::Slave::init ( void ) const
59 {
60 #ifdef USE_MPI
61     MPI_Comm_rank ( MPI_COMM_WORLD, &NOMAD::Slave::_rank );
62     MPI_Comm_size ( MPI_COMM_WORLD, &NOMAD::Slave::_np   );
63 #else
64     NOMAD::Slave::_rank = 0;
65     NOMAD::Slave::_np   = 1;
66 #endif
67 
68     // Slave::force_quit() will be called if ctrl-c is pressed:
69     if ( !NOMAD::Slave::is_master() ) {
70 
71         NOMAD::Evaluator::force_quit();
72 
73         signal ( SIGTERM , NOMAD::Slave::force_quit );
74         signal ( SIGINT  , NOMAD::Slave::force_quit );
75 #ifndef WINDOWS
76         signal ( SIGPIPE , NOMAD::Slave::force_quit ); // (ctrl-c during a "| more")
77 #endif
78     }
79 }
80 
81 /*----------------------------------------*/
82 /*          get the process rank          */
83 /*               (static)                 */
84 /*----------------------------------------*/
get_rank(void)85 int NOMAD::Slave::get_rank ( void )
86 {
87     if ( NOMAD::Slave::_rank < 0 ) {
88 #ifdef USE_MPI
89         MPI_Comm_rank ( MPI_COMM_WORLD, &NOMAD::Slave::_rank );
90 #else
91         NOMAD::Slave::_rank = 0;
92 #endif
93     }
94     return NOMAD::Slave::_rank;
95 }
96 
97 /*----------------------------------------*/
98 /*       get the number of processes      */
99 /*               (static)                 */
100 /*----------------------------------------*/
get_nb_processes(void)101 int NOMAD::Slave::get_nb_processes ( void )
102 {
103     if ( NOMAD::Slave::_np < 0 ) {
104 #ifdef USE_MPI
105         MPI_Comm_size ( MPI_COMM_WORLD, &NOMAD::Slave::_np );
106 #else
107         NOMAD::Slave::_np = 1;
108 #endif
109     }
110     return NOMAD::Slave::_np;
111 }
112 
113 /*----------------------*/
114 /*  run the slave code  */
115 /*----------------------*/
run(void) const116 void NOMAD::Slave::run ( void ) const
117 {
118 #ifdef USE_MPI
119 
120     MPI_Request         req;
121     char                signal     = 0;
122     NOMAD::Eval_Point * x          = NULL;
123     bool                count_eval = false;
124 
125     while ( true ) {
126 
127         // receive signal from master:
128         // ---------------------------
129         NOMAD::Slave::receive_data ( &signal , 1 , MPI_CHAR , 0 , &req );
130 
131         // slave is ready or not initialized:
132         NOMAD::Slave::send_data ( &NOMAD::READY_SIGNAL , 1 , MPI_CHAR , 0 , false );
133 
134         NOMAD::Slave::wait_request ( req );
135 
136         // EVAL signal:
137         // ------------
138         if ( signal == NOMAD::EVAL_SIGNAL ) {
139 
140             // receive and evaluate the point:
141             x = eval_point ( count_eval );
142 
143         }
144 
145         // RESULT signal:
146         // --------------
147         else if ( signal == NOMAD::RESULT_SIGNAL )
148         {
149 
150             // send the evaluation result to the master:
151             send_eval_result ( x , count_eval );
152 
153             delete x;
154             x = NULL;
155         }
156 
157         // STOP signal:
158         // ------------
159         else if ( signal == NOMAD::STOP_SIGNAL )
160             break;
161 
162         // WAIT signal:
163         // ------------
164         // else if ( signal == NOMAD::WAIT_SIGNAL ) {
165         // }
166     }
167 
168     if ( x )
169         delete x;
170 
171 #endif
172 }
173 
174 /*-----------------------------------------*/
175 /*        initialize all the slaves        */
176 /*                (static)                 */
177 /*-----------------------------------------*/
init_slaves(const NOMAD::Display & out)178 void NOMAD::Slave::init_slaves ( const NOMAD::Display & out )
179 {
180 #ifdef USE_MPI
181 
182     if ( !NOMAD::Slave::is_master() || NOMAD::Slave::_are_running )
183         return;
184 
185     NOMAD::dd_type display_degree = out.get_gen_dd();
186     if ( display_degree == NOMAD::FULL_DISPLAY )
187         out << std::endl << NOMAD::open_block ( "initializing slaves" );
188 
189     MPI_Status     status;
190     MPI_Request ** req            = new MPI_Request * [ NOMAD::Slave::_np ];
191     int            nb_initialized = 0;
192     int            nb_slaves      = NOMAD::Slave::_np - 1;
193     int            source;
194     char           signal;
195     NOMAD::Clock   clk;
196 
197     // 1. launch requests:
198     for ( source = 1 ; source < NOMAD::Slave::_np ; ++source ) {
199         req[source] = new MPI_Request;
200         NOMAD::Slave::receive_data ( &signal , 1 , MPI_CHAR , source ,  req[source] );
201         if ( display_degree == NOMAD::FULL_DISPLAY )
202             out << "." << std::endl;
203     }
204 
205     // 2. test requests (with a maximal delay of MAX_REQ_WAIT):
206     int cnt = 0 , flag;
207     while ( nb_initialized < nb_slaves && clk.get_real_time() < NOMAD::MAX_REQ_WAIT )
208     {
209 
210         for ( source = 1 ; source < NOMAD::Slave::_np ; ++source )
211         {
212 
213             if ( req[source] )
214             {
215 
216                 MPI_Test ( req[source] , &flag , &status );
217 
218                 if ( flag )
219                 {
220 
221                     MPI_Wait ( req[source] , &status );
222 
223                     // send the WAIT signal:
224                     NOMAD::Slave::send_data ( &NOMAD::WAIT_SIGNAL , 1 , MPI_CHAR , source , true );
225 
226                     delete req[source];
227                     req[source] = NULL;
228                     ++nb_initialized;
229                 }
230             }
231         }
232         // a constant is used in order to display only a few '.' :
233         if ( display_degree == NOMAD::FULL_DISPLAY && cnt%1000000==0 )
234             out << "." << std::endl;
235 
236         ++cnt;
237     }
238 
239     // 3. delete requests:
240     std::list<int> err_list;
241     for ( source = 1 ; source < NOMAD::Slave::_np ; ++source )
242     {
243         if ( req[source] )
244         {
245             err_list.push_back ( source );
246             MPI_Cancel ( req[source] );
247             delete req[source];
248         }
249     }
250     delete [] req;
251 
252     NOMAD::Slave::_are_running = true;
253     NOMAD::Slave::_stop_ok     = false;
254 
255     if ( display_degree == NOMAD::FULL_DISPLAY )
256         out << NOMAD::close_block() << std::endl;
257 
258     if ( !err_list.empty() )
259     {
260 
261         std::ostringstream oss;
262         oss << "could not initialize slave";
263         if ( err_list.size() > 1 )
264         {
265             oss << "s";
266             std::list<int>::const_iterator it , end = err_list.end();
267             for ( it = err_list.begin() ; it != end ; ++it )
268                 oss << " #" << *it;
269         }
270         else
271             oss << " #" << *err_list.begin();
272 
273         throw NOMAD::Exception ( "Slave.cpp" , __LINE__ , oss.str() );
274     }
275 
276 #endif
277 }
278 
279 /*-----------------------------------------*/
280 /*             stop the slaves             */
281 /*                (static)                 */
282 /*-----------------------------------------*/
stop_slaves(const NOMAD::Display & out)283 void NOMAD::Slave::stop_slaves ( const NOMAD::Display & out )
284 {
285 #ifdef USE_MPI
286 
287     if ( !NOMAD::Slave::is_master() || NOMAD::Slave::_stop_ok )
288         return;
289 
290     NOMAD::dd_type display_degree = out.get_gen_dd();
291     if ( display_degree == NOMAD::FULL_DISPLAY )
292         out << std::endl << NOMAD::open_block ( "stopping slaves" );
293 
294     int  nb_stopped = 0;
295     int  nb_slaves  = NOMAD::Slave::_np - 1;
296     int  source;
297     char signal;
298 
299     NOMAD::Clock clk;
300 
301     MPI_Status  status;
302     MPI_Request ** req = new MPI_Request * [ NOMAD::Slave::_np ];
303 
304     // 1. launch requests:
305     for ( source = 1 ; source < NOMAD::Slave::_np ; ++source ) {
306         req[source] = new MPI_Request;
307         NOMAD::Slave::receive_data ( &signal , 1 , MPI_CHAR , source ,  req[source] );
308         if ( display_degree == NOMAD::FULL_DISPLAY )
309             out << "." << std::endl;
310     }
311 
312     // 2. test requests (with a maximal delay of MAX_REQ_WAIT):
313     int cnt = 0 , flag;
314     while ( nb_stopped < nb_slaves && clk.get_real_time() < NOMAD::MAX_REQ_WAIT ) {
315 
316         for ( source = 1 ; source < NOMAD::Slave::_np ; ++source ) {
317 
318             if ( req[source] ) {
319 
320                 MPI_Test ( req[source] , &flag , &status );
321 
322                 if ( flag ) {
323 
324                     MPI_Wait ( req[source] , &status );
325 
326                     // send the STOP signal:
327                     NOMAD::Slave::send_data ( &NOMAD::STOP_SIGNAL , 1 , MPI_CHAR , source , true );
328 
329                     delete req[source];
330                     req[source] = NULL;
331                     ++nb_stopped;
332                 }
333             }
334         }
335         // a constant is used in order to display only a few '.' :
336         if ( display_degree == NOMAD::FULL_DISPLAY && cnt%1000000==0 )
337             out << "." << std::endl;
338         ++cnt;
339     }
340 
341     NOMAD::Slave::_are_running = false;
342     NOMAD::Slave::_stop_ok     = true;
343 
344     // 3. delete requests:
345     for ( source = 1 ; source < NOMAD::Slave::_np ; ++source ) {
346         if ( req[source] ) {
347             MPI_Cancel ( req[source] );
348             delete req[source];
349             NOMAD::Slave::_stop_ok = false;
350         }
351     }
352     delete [] req;
353 
354     if ( display_degree == NOMAD::FULL_DISPLAY )
355         out << NOMAD::close_block() << std::endl;
356 
357 #endif
358 }
359 
360 #ifdef USE_MPI
361 
362 /*------------------------------------------------------*/
363 /*               receive data (static, private)         */
364 /*------------------------------------------------------*/
receive_data(void * buf,int count,MPI_Datatype datatype,int source,MPI_Request * req)365 int NOMAD::Slave::receive_data ( void        * buf      ,
366                                 int           count    ,
367                                 MPI_Datatype  datatype ,
368                                 int           source   ,  // may be MPI_ANY_SOURCE
369                                 MPI_Request * req        )
370 {
371     int tag = ( NOMAD::Slave::is_master() ) ? source : NOMAD::Slave::get_rank();
372 
373     // immediate receive:
374     if ( req ) {
375         if ( source == MPI_ANY_SOURCE )
376             throw NOMAD::Exception ( "Slave.cpp" , __LINE__ ,
377                                     "Slave::receive_data(): immediate receive with no source" );
378         MPI_Irecv ( buf , count , datatype , source , tag , MPI_COMM_WORLD , req );
379     }
380 
381     // normal receive:
382     else {
383         MPI_Status status;
384         if ( source == MPI_ANY_SOURCE )
385             tag = MPI_ANY_TAG;
386         MPI_Recv ( buf , count , datatype , source , tag , MPI_COMM_WORLD , &status );
387         source = status.MPI_SOURCE;
388     }
389 
390     // stats:
391     int size;
392     MPI_Type_size ( datatype , &size );
393     NOMAD::Slave::_data_rcvd += count * size;
394 
395     return source;
396 }
397 
398 /*------------------------------------------------------*/
399 /*              send data (static, private)             */
400 /*------------------------------------------------------*/
send_data(const void * buf,int count,MPI_Datatype datatype,int dest,bool ready_send)401 void NOMAD::Slave::send_data ( const void  * buf        ,
402                               int           count      ,
403                               MPI_Datatype  datatype   ,
404                               int           dest       ,
405                               bool          ready_send   )
406 {
407     int tag = ( NOMAD::Slave::is_master() ) ? dest : NOMAD::Slave::get_rank();
408 
409     // ready send:
410     if ( ready_send )
411         MPI_Rsend ( const_cast<void*>(buf) , count , datatype ,
412                    dest , tag , MPI_COMM_WORLD );
413 
414     // normal send:
415     else
416         MPI_Send ( const_cast<void*>(buf) , count , datatype ,
417                   dest , tag , MPI_COMM_WORLD );
418 
419     // stats:
420     int size;
421     MPI_Type_size ( datatype , &size );
422     NOMAD::Slave::_data_sent += count * size;
423 }
424 
425 /*------------------------------------------------------*/
426 /*  receive and evaluate an Eval_Point from the master  */
427 /*  (private)                                           */
428 /*------------------------------------------------------*/
eval_point(bool & count_eval) const429 NOMAD::Eval_Point * NOMAD::Slave::eval_point ( bool & count_eval ) const
430 {
431     // 1. receive the point:
432     int         itab[3];
433     MPI_Request req;
434     NOMAD::Slave::receive_data ( itab                 , 3 , MPI_INT  , 0 , &req  );
435     NOMAD::Slave::send_data    ( &NOMAD::READY_SIGNAL , 1 , MPI_CHAR , 0 , false );
436     NOMAD::Slave::wait_request ( req );
437 
438     int      n    = itab[0];
439     double * dtab = new double[n+1];
440 
441     NOMAD::Slave::receive_data ( dtab                 , n+1 , MPI_DOUBLE , 0 , &req  );
442     NOMAD::Slave::send_data    ( &NOMAD::READY_SIGNAL , 1   , MPI_CHAR   , 0 , false );
443     NOMAD::Slave::wait_request ( req );
444 
445     // 2. create the Eval_Point:
446     int bb_nb_outputs=_p->get_bb_nb_outputs();
447     NOMAD::Eval_Point * x = new NOMAD::Eval_Point ( n , bb_nb_outputs );
448     for ( int i = 0 ; i < n ; ++i )
449         (*x)[i] = dtab[i];
450     NOMAD::Double h_max = dtab[n];
451 
452     x->set_tag       ( itab[2]                            );
453     x->set_eval_type ( ( itab[1] > 0 ) ? NOMAD::SGTE : NOMAD::TRUTH );
454 
455     delete [] dtab;
456 
457     // 3. evaluate the point:
458     bool eval_ok;
459     try {
460         eval_ok = _ev->eval_x ( *x , h_max , count_eval );
461     }
462     catch ( ... ) {
463         eval_ok = false;
464     }
465 
466     x->set_eval_status ( ( eval_ok ) ? NOMAD::EVAL_OK : NOMAD::EVAL_FAIL );
467 
468     return x;
469 }
470 
471 /*-----------------------------------------------------*/
472 /*  send an evaluation result to the master (private)  */
473 /*-----------------------------------------------------*/
send_eval_result(const NOMAD::Eval_Point * x,bool count_eval) const474 void NOMAD::Slave::send_eval_result ( const NOMAD::Eval_Point * x          ,
475                                      bool                      count_eval   ) const
476 {
477     // receive a signal from the master:
478     char signal;
479     NOMAD::Slave::receive_data ( &signal , 1 , MPI_CHAR , 0 , NULL );
480 
481     // send the evaluation result:
482     int                  m    = _p->get_bb_nb_outputs();
483     int                  s    = 2*m+2;
484     double             * dtab = new double [s];
485     const NOMAD::Point & bbo  = x->get_bb_outputs();
486 
487     // bb_outputs (m values):
488     for ( int i = 0 ; i < m ; ++i ) {
489         if ( bbo[i].is_defined() ) {
490             dtab[i  ] = bbo[i].value();
491             dtab[i+m] = 1.0;
492         }
493         else {
494             dtab[i  ] = NOMAD::INF;
495             dtab[i+m] = -1.0;
496         }
497     }
498 
499     // evaluation status:
500     dtab[2*m] = ( x->get_eval_status() == NOMAD::EVAL_OK ) ? 1.0 : -1.0;
501 
502     // count_eval:
503     dtab[s-1] = ( count_eval ) ? 1.0 : -1.0;
504 
505     // send the array:
506     NOMAD::Slave::send_data ( dtab , s , MPI_DOUBLE , 0 , true );
507 
508     delete [] dtab;
509 }
510 
511 /*---------------------------------------------*/
512 /*  receive an evaluation result from a slave  */
513 /*---------------------------------------------*/
receive_eval_result(int slave_rank,NOMAD::Eval_Point * x,bool & eval_ok,bool & count_eval) const514 void NOMAD::Slave::receive_eval_result ( int                 slave_rank ,
515                                         NOMAD::Eval_Point * x          ,
516                                         bool              & eval_ok    ,
517                                         bool              & count_eval    ) const
518 {
519     // send the RESULT signal to the slave:
520     NOMAD::Slave::send_data ( &NOMAD::RESULT_SIGNAL , 1 , MPI_CHAR , slave_rank , true );
521 
522     // receive the evaluation result as a double array:
523     int      m    =  _p->get_bb_nb_outputs();
524     int      s    = 2*m+2;
525     double * dtab = new double [s];
526 
527     MPI_Request req;
528     NOMAD::Slave::receive_data ( dtab              , s , MPI_DOUBLE , slave_rank , &req  );
529     NOMAD::Slave::send_data ( &NOMAD::READY_SIGNAL , 1 , MPI_CHAR   , slave_rank , false );
530     NOMAD::Slave::wait_request ( req );
531 
532     // interpret the array:
533     for ( int i = 0 ; i < m ; ++i )
534         x->set_bb_output ( i , ( dtab[i+m] > 0.0 ) ? dtab[i] : NOMAD::Double() );
535 
536     eval_ok = ( dtab[2*m] > 0.0 );
537 
538     x->set_eval_status ( eval_ok ? NOMAD::EVAL_OK : NOMAD::EVAL_FAIL );
539 
540     count_eval = ( dtab[s-1] > 0.0 );
541 
542     delete [] dtab;
543 }
544 
545 /*-----------------------------------------------------*/
546 /*            send an Eval_Point to a slave            */
547 /*-----------------------------------------------------*/
send_eval_point(const NOMAD::Eval_Point * x,int slave_rank,const NOMAD::Double & h_max) const548 void NOMAD::Slave::send_eval_point ( const NOMAD::Eval_Point * x          ,
549                                     int                       slave_rank ,
550                                     const NOMAD::Double     & h_max        ) const
551 {
552     char signal;
553     int  itab[3];
554     int  n = x->size();
555 
556     // n:
557     itab[0] = n;
558 
559     // evaluation type (+1: sgte eval; -1: true eval):
560     itab[1] = ( x->get_eval_type() == NOMAD::SGTE ) ? 1 : -1;
561 
562     // tag of the point:
563     itab[2] = x->get_tag();
564 
565     // point coordinates:
566     double * dtab = new double[n+1];
567     for ( int i = 0 ; i < n ; ++i )
568         dtab[i] = (*x)[i].value();
569     dtab[n] = h_max.value();
570 
571     // wait for the slave signal:
572     NOMAD::Slave::receive_data ( &signal , 1 , MPI_CHAR , slave_rank , NULL );
573 
574     // send n and evaluation type:
575     NOMAD::Slave::send_data ( itab , 3 , MPI_INT , slave_rank , true );
576 
577     // wait for the slave signal:
578     NOMAD::Slave::receive_data ( &signal , 1 , MPI_CHAR , slave_rank , NULL );
579 
580     // send the point coordinates:
581     NOMAD::Slave::send_data ( dtab , n+1 , MPI_DOUBLE , slave_rank , true );
582 
583     delete [] dtab;
584 }
585 
586 /*-----------------------------------------*/
587 /*          wait for a MPI request         */
588 /*                (static)                 */
589 /*-----------------------------------------*/
wait_request(MPI_Request & req)590 void NOMAD::Slave::wait_request ( MPI_Request & req )
591 {
592     MPI_Status status;
593     MPI_Wait ( &req , &status );
594 }
595 
596 #endif
597