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