233 using Node =
typename P::node_type;
246 MPI_Comm comm = MPI_COMM_WORLD )
247 : f_p( problem ) , f_options( options ) , f_comm( comm ) {
257 MPI_Comm_rank( f_comm , & rank );
259 return( run( std::nullopt ) );
260 return( run( std::vector< Leaf >() ) );
268 return( run( std::move( seeds ) ) );
275 SolutionT run( std::optional< std::vector< Leaf > > seeds )
const
278 Process w( *
this , clock );
279 w.run( detail::bb_starts( f_p , std::move( seeds ) ) );
286 using Item =
typename BBQueue< Node , Value >::Item;
289 enum Tag : std::uint32_t { c_tag_work = 1 , c_tag_incumbent = 2 };
297 : f_a( a ) , f_comm( a.f_comm ) , f_mail( a.f_comm , 1 ) , f_clock( c ) ,
298 f_q( a.f_options.selection ) , f_fr( a.f_options.keep_frontier ) {
299 MPI_Comm_rank( f_comm , & f_rank );
301 MPI_Comm_size( f_comm , & np );
302 f_np = unsigned( np );
303 v_out.resize( f_np );
304 v_count.assign( f_np , 0 );
305 f_gen.seed( a.f_options.seed +
unsigned( f_rank ) );
306 f_inc = a.f_options.incumbent ?
307 detail::bb_signed< P >( *a.f_options.incumbent ) :
308 BBValueTraits<
Value >::infinity();
309 const std::size_t lim = a.f_options.node_limit;
310 f_limit = lim ? ( lim + f_np - 1 ) / f_np : 0;
314 void run( std::vector< Leaf > && starts ) {
315 auto w = detail::bb_worker( f_a.f_p ,
unsigned( f_rank ) );
316 const auto & o = f_a.f_options;
317 const auto inc = [ & ] {
return( f_inc ); };
318 const auto improve = [ & ](
Value v ,
const Node & n ) {
321 f_inc = f_node_value = v;
324 o.on_incumbent( detail::bb_signed< P >( v ) );
325 std::vector< char > b;
326 detail::bb_put_bytes( v , b );
327 for(
unsigned q = 0 ; q < f_np ; ++q )
328 if( q !=
unsigned( f_rank ) )
329 f_mail.send( q , c_tag_incumbent , std::as_bytes( std::span( b ) ) );
333 for(
auto & l : starts )
334 push( Item{ std::move( l.node ) , l.bound , l.depth } );
336 unsigned since_flush = 0;
339 if( std::optional< Item > it = next() ) {
341 detail::bb_process< P >( it->node , it->bound , it->depth , w , inc ,
344 v_kids.push_back( Item{ std::move( c ) , b ,
346 } , o , f_st , f_fr );
347 const std::size_t k = detail::bb_dive( v_kids , f_q.best_bound() ,
349 for( std::size_t j = 0 ; j < v_kids.size() ; ++j )
351 f_dive.emplace( std::move( v_kids[ j ] ) );
353 send( std::move( v_kids[ j ] ) );
354 if( ++since_flush >= f_a.f_options.flush_period ) {
362 if( f_mail.quiescent() )
366 f_mail.leftovers( [ & ]( std::uint32_t t ,
367 std::span< const std::byte > b ) {
368 take( t , b ,
true );
373 void push( Item && it ) {
374 const Value key = detail::bb_key( f_a.f_p , it.node , it.bound ,
375 it.depth , f_a.f_options.selection );
376 f_q.push( std::move( it.node ) , it.bound , it.depth , key );
377 f_st.max_open = std::max( f_st.max_open , f_q.size() );
382 bool prunable(
void ) {
385 const Value best = f_q.best_bound();
386 if( ! detail::bb_prunable( best , f_inc , f_a.f_options ) )
394 std::optional< Item > next(
void ) {
395 if( f_mail.stopped() )
396 return( std::nullopt );
398 std::optional< Item > it( std::move( f_dive ) );
403 return( std::nullopt );
408 void check_limits(
void ) {
409 const auto & o = f_a.f_options;
410 const bool nodes = f_limit && ( f_st.evaluated >= f_limit );
411 const bool time = ( o.time_limit > 0 ) &&
412 ( f_clock.seconds() >= o.time_limit );
413 if( f_mail.stopped() || ! ( nodes || time ) )
421 void send( Item && it ) {
423 if constexpr( HashedBranchAndBound< P > )
424 dst =
unsigned( std::uint64_t( f_a.f_p.hash( it.node ) ) % f_np );
427 if( dst ==
unsigned( f_rank ) ) {
428 push( std::move( it ) );
431 auto & o = v_out[ dst ];
432 detail::bb_put_bytes( it.bound , o );
433 detail::bb_put_bytes( it.depth , o );
434 detail::bb_put_node( f_a.f_p , it.node , o );
436 if( ++v_count[ dst ] >= f_a.f_options.batch )
441 void post(
unsigned dst ) {
442 f_mail.send( dst , c_tag_work ,
443 std::as_bytes( std::span( v_out[ dst ] ) ) );
444 v_out[ dst ].clear();
451 for(
unsigned d = 0 ; d < f_np ; ++d )
452 if( ! v_out[ d ].empty() )
457 void receive(
void ) {
458 f_mail.poll( [ & ]( std::uint32_t t , std::span< const std::byte > b ) {
459 take( t , b ,
false );
465 void take( std::uint32_t t , std::span< const std::byte > b ,
bool open ) {
466 const char * in =
reinterpret_cast< const char *
>( b.data() );
467 const char * end = in + b.size();
468 if( t == c_tag_incumbent ) {
469 f_inc = std::min( f_inc , detail::bb_get_bytes< Value >( in ) );
473 const Value v = detail::bb_get_bytes< Value >( in );
474 const unsigned d = detail::bb_get_bytes< unsigned >( in );
475 Node n = detail::bb_get_node( f_a.f_p , in );
479 push( Item{ std::move( n ) , v , d } );
486 BBStats & st = sol.stats;
487 unsigned long long mine[ 11 ] = {
488 f_st.evaluated , f_st.branched , f_st.generated , f_st.infeasible ,
489 f_st.pruned_generated , f_st.pruned_extracted , f_st.pruned_evaluated ,
490 f_st.incumbents , f_st.sent , f_st.messages , f_st.max_open };
491 std::vector< unsigned long long > all( 11 * std::size_t( f_np ) );
492 MPI_Allgather( mine , 11 , MPI_UNSIGNED_LONG_LONG , all.data() , 11 ,
493 MPI_UNSIGNED_LONG_LONG , f_comm );
494 for(
unsigned q = 0 ; q < f_np ; ++q ) {
495 const auto * x = all.data() + 11 * std::size_t( q );
497 s.evaluated = x[ 0 ];
499 s.generated = x[ 2 ];
500 s.infeasible = x[ 3 ];
501 s.pruned_generated = x[ 4 ];
502 s.pruned_extracted = x[ 5 ];
503 s.pruned_evaluated = x[ 6 ];
504 s.incumbents = x[ 7 ];
507 s.max_open = std::size_t( x[ 10 ] );
511 int flags[ 2 ] = { f_limit_hit ? 1 : 0 , f_time_hit ? 1 : 0 } , any[ 2 ];
512 MPI_Allreduce( flags , any , 2 , MPI_INT , MPI_MAX , f_comm );
517 f_fr.add( std::move( f_dive->node ) , f_dive->bound , f_dive->depth ,
521 using Tr = BBValueTraits< Value >;
522 const Value glow = detail::bb_all_min( f_fr.low , f_comm ).first;
525 const auto [ wv , wr ] = detail::bb_all_min( f_node ? f_node_value : inf ,
527 const Value z = detail::bb_all_min( f_inc , f_comm ).first;
528 if( Tr::finite( wv ) && ( wv <= z ) ) {
529 std::vector< char > bytes;
531 detail::bb_put_node( f_a.f_p , *f_node , bytes );
532 unsigned long long n = bytes.size();
533 MPI_Bcast( & n , 1 , MPI_UNSIGNED_LONG_LONG , wr , f_comm );
534 bytes.resize( std::size_t( n ) );
535 MPI_Bcast( bytes.data() ,
int( n ) , MPI_BYTE , wr , f_comm );
536 const char * in = bytes.data();
537 sol.node.emplace( detail::bb_get_node( f_a.f_p , in ) );
544 if( Tr::finite( z ) )
545 sol.value = detail::bb_signed< P >( z );
546 const Value lb = std::min( z , glow );
547 sol.bound = detail::bb_signed< P >( lb );
548 sol.gap = Tr::finite( z ) && Tr::finite( lb ) ?
Value( z - lb ) : inf;
549 f_fr.template take< P >( sol.frontier );
554 ADS::MPIByteMailbox f_mail;
555 const Stopwatch & f_clock;
558 BBQueue< Node , Value > f_q;
559 std::optional< Item > f_dive;
560 std::vector< Item > v_kids;
561 std::vector< std::vector< char > > v_out;
562 std::vector< std::size_t > v_count;
563 std::mt19937_64 f_gen;
565 std::optional< Node > f_node;
568 bool f_limit_hit =
false;
569 bool f_time_hit =
false;
571 detail::BBFrontier< Node , Value > f_fr;