PBeFS
Parallel Best-First Search
Loading...
Searching...
No Matches
ParallelBranchAndBound.h
Go to the documentation of this file.
1/*--------------------------------------------------------------------------*/
2/*--------------------- File ParallelBranchAndBound.h ----------------------*/
3/*--------------------------------------------------------------------------*/
84/*--------------------------------------------------------------------------*/
85/*------------------------------ DEFINITIONS -------------------------------*/
86/*--------------------------------------------------------------------------*/
87
88#ifndef __PBeFS_ParallelBranchAndBound
89 #define __PBeFS_ParallelBranchAndBound
90 /* self-identification: #endif at the end of the file */
91
92/*--------------------------------------------------------------------------*/
93/*-------------------------------- INCLUDES --------------------------------*/
94/*--------------------------------------------------------------------------*/
95
96#include <atomic>
97
98#include <memory>
99
100#include <mutex>
101
102#include <random>
103
104#include <type_traits>
105
107
108#include "ADS/parallel/Channels.h"
109
110#include "ADS/parallel/Mailbox.h"
111
112/*--------------------------------------------------------------------------*/
113/*------------------------------- NAMESPACE --------------------------------*/
114/*--------------------------------------------------------------------------*/
115
117namespace PBeFS_di_unipi_it
118{
119
120/*--------------------------------------------------------------------------*/
121/*------------------------ CLASS ParallelBBOptions -------------------------*/
122/*--------------------------------------------------------------------------*/
124
132
133template< class V = double >
135 public ParallelParams {
136
138 batch = 16;
139 flush_period = 1;
140 }
141
144 std::size_t round = 0;
145
148 std::size_t split = 0;
149
153 bool split_dive = true;
154
158 bool split_shared = false;
159
160 }; // end( struct( BasicParallelBBOptions ) )
161
164
165namespace detail {
166
168template< class V , bool = std::is_trivially_copyable_v< V > >
169struct bb_lock_free : std::false_type {};
170
171template< class V >
172struct bb_lock_free< V , true >
173 : std::bool_constant< std::atomic< V >::is_always_lock_free > {};
174
175} // end( namespace detail )
176
177/*--------------------------------------------------------------------------*/
178/*--------------------------- CLASS BBIncumbent ----------------------------*/
179/*--------------------------------------------------------------------------*/
181
186
187template< class Node , class V = double >
189
190 public:
191
194
195 explicit BBIncumbent( V v ) : f_value( v ) {}
196
197/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
199
200 [[nodiscard]] V value( void ) const {
201 if constexpr( c_atomic )
202 return( f_value.load( std::memory_order_relaxed ) );
203 else {
204 std::lock_guard< std::mutex > guard( f_m );
205 return( f_value );
206 }
207 }
208
209/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
212
213 template< class P >
214 bool improve( V v , const Node & n , const BasicBBOptions< V > & o ) {
215 if( c_atomic && ( v >= value() ) )
216 return( false );
217 std::lock_guard< std::mutex > guard( f_m );
218 if( v >= stored() )
219 return( false );
220 if constexpr( c_atomic )
221 f_value.store( v , std::memory_order_relaxed );
222 else
223 f_value = v;
224 f_node = n;
225 if( o.on_incumbent )
226 o.on_incumbent( detail::bb_signed< P >( v ) );
227 return( true );
228 }
229
230/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
232
233 std::optional< Node > take_node( void ) { return( std::move( f_node ) ); }
234
235 private:
236
238 static constexpr bool c_atomic = detail::bb_lock_free< V >::value;
239
240 // the value, read under the lock
241 [[nodiscard]] V stored( void ) const {
242 if constexpr( c_atomic )
243 return( f_value.load( std::memory_order_relaxed ) );
244 else
245 return( f_value );
246 }
247
248 alignas( cache_line ) std::conditional_t< c_atomic , std::atomic< V > , V >
249 f_value;
250 mutable std::mutex f_m;
251 std::optional< Node > f_node;
252
253 }; // end( class( BBIncumbent ) )
254
255/*--------------------------------------------------------------------------*/
256/*------------------------------- FUNCTIONS --------------------------------*/
257/*--------------------------------------------------------------------------*/
258
259namespace detail {
260
263
264template< BranchAndBound P >
265void bb_finish( BBSolution< typename P::node_type , bb_value_t< P > > & sol ,
266 BBIncumbent< typename P::node_type , bb_value_t< P > > & inc ,
267 BBFrontier< typename P::node_type , bb_value_t< P > > & fr ,
268 bool limit , bool time )
269{
270 using V = bb_value_t< P >;
271 using Tr = BBValueTraits< V >;
272 const V z = inc.value();
273 if( limit )
274 sol.status = time ? BBStatus::time_limit : BBStatus::node_limit;
275 else
276 sol.status = Tr::finite( z ) ? BBStatus::optimal : BBStatus::infeasible;
277 sol.node = inc.take_node();
278 if( Tr::finite( z ) )
279 sol.value = bb_signed< P >( z );
280 const V lb = std::min( z , fr.low );
281 sol.bound = bb_signed< P >( lb );
282 sol.gap = Tr::finite( z ) && Tr::finite( lb ) ? V( z - lb )
283 : Tr::infinity();
284 fr.template take< P >( sol.frontier );
285 }
286
287/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
292
293template< class Item , class V >
294std::size_t bb_dive( const std::vector< Item > & kids , V best , V inc ,
295 const BasicBBOptions< V > & o )
296{
297 if( ( o.selection == Selection::depth_first ) && ! kids.empty() )
298 return( kids.size() - 1 );
299 if( ( o.selection != Selection::plunging ) || kids.empty() )
300 return( kids.size() );
301 std::size_t c = 0;
302 for( std::size_t j = 1 ; j < kids.size() ; ++j )
303 if( kids[ j ].bound <= kids[ c ].bound )
304 c = j;
305 using Tr = BBValueTraits< V >;
306 const V b0 = std::min( best , kids[ c ].bound );
307 if( ! Tr::finite( inc ) ||
308 Tr::within( kids[ c ].bound , b0 , inc , o.plunge_gap ) )
309 return( c );
310 return( kids.size() );
311 }
312
313/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
317
318template< class V >
319bool bb_limit( const BasicBBOptions< V > & o , std::uint64_t evaluated ,
320 const Stopwatch & clock , std::atomic< bool > & limit ,
321 std::atomic< bool > & time )
322{
323 const bool nodes = o.node_limit && ( evaluated >= o.node_limit );
324 const bool late = ( o.time_limit > 0 ) && ( clock.seconds() >= o.time_limit );
325 if( ! ( nodes || late ) )
326 return( false );
327 if( late )
328 time.store( true );
329 limit.store( true );
330 return( true );
331 }
332
333} // end( namespace detail )
334
335/*--------------------------------------------------------------------------*/
336/*----------------------- CLASS SharedBranchAndBound -----------------------*/
337/*--------------------------------------------------------------------------*/
339
346
347template< BranchAndBound P , ADS::WorkerExecutor Executor = ADS::ThreadPool >
349
350/*------------------------ PUBLIC PART OF THE CLASS ------------------------*/
351
352 public:
353
354/*------------------------------ PUBLIC TYPES ------------------------------*/
355
356 using Node = typename P::node_type;
361
362/*------------------------------ CONSTRUCTOR -------------------------------*/
363
365
366 explicit SharedBranchAndBound( const P & problem ,
367 const Options & options = {} )
368 : f_p( problem ) , f_options( options ) {
369 f_options.normalize();
370 }
371
372/*------------------------- OTHER INITIALIZATIONS --------------------------*/
376
377 void set_executor( Executor & ex ) noexcept { f_ex = &ex; }
378
379/*-------------------------------- SOLVING ---------------------------------*/
381
382 [[nodiscard]] SolutionT solve( void ) const {
383 return( run( std::nullopt ) );
384 }
385
386/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
389
390 [[nodiscard]] SolutionT solve( std::vector< Leaf > seeds ) const {
391 return( run( std::move( seeds ) ) );
392 }
393
394/*----------------------- PRIVATE PART OF THE CLASS ------------------------*/
395
396 private:
397
398 SolutionT run( std::optional< std::vector< Leaf > > seeds ) const
399 {
401 const unsigned p = f_options.threads;
402 const Selection sel = f_options.selection;
403 Stopwatch clock;
404 const Value start = f_options.incumbent ?
405 detail::bb_signed< P >( *f_options.incumbent ) : inf;
406 Shared sh( start , sel );
407 for( auto & l : detail::bb_starts( f_p , std::move( seeds ) ) ) {
408 const Value key = detail::bb_key( f_p , l.node , l.bound , l.depth ,
409 sel );
410 sh.q.push( std::move( l.node ) , l.bound , l.depth , key );
411 }
412 sh.open.store( sh.q.size() );
413 sh.max_open = sh.q.size();
414
415 std::vector< std::unique_ptr< Worker > > w( p );
416 for( unsigned i = 0 ; i < p ; ++i )
417 w[ i ] = std::make_unique< Worker >( *this , sh , clock );
418 run_workers( f_ex , p , [ & ]( unsigned i ) { w[ i ]->run( i ); } );
419
420 SolutionT sol;
421 detail::BBFrontier< Node , Value > fr( f_options.keep_frontier );
422 fr.add( sh.q , BBLeafKind::open ); // left open by a limit
423 for( auto & x : w ) {
424 sol.stats.add( x->f_st );
425 fr.merge( std::move( x->f_fr ) );
426 }
427 sol.stats.max_open = sh.max_open;
428 detail::bb_finish< P >( sol , sh.inc , fr , sh.limit.load() ,
429 sh.time.load() );
430 sol.stats.seconds = clock.seconds();
431 return( sol );
432 }
433
434/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
435
436 using Lock = typename Executor::lock_type;
437 using Item = typename BBQueue< Node , Value >::Item;
438
439/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
440 // the data shared by the workers
441
442 struct Shared {
443
444 Shared( Value z , Selection s ) : inc( z ) , q( s ) {}
445
446 BBIncumbent< Node , Value > inc; // the incumbent
447 alignas( cache_line ) Lock lock; // protects q and max_open
448 BBQueue< Node , Value > q; // the open nodes
449 std::size_t max_open = 0; // the most open nodes
450 alignas( cache_line ) std::atomic< std::size_t > open{ 0 }; // q.size()
451 alignas( cache_line ) std::atomic< unsigned > busy{ 0 }; // holding a node
452 alignas( cache_line ) std::atomic< std::uint64_t > evaluated{ 0 };
453 alignas( cache_line ) std::atomic< bool > done{ false };
454 std::atomic< bool > limit{ false }; // stopped by a limit
455 std::atomic< bool > time{ false }; // ... the time limit
456 };
457
458/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
459 // a worker
460
461 struct alignas( cache_line ) Worker {
462
463 Worker( const SharedBranchAndBound & a , Shared & s , const Stopwatch & c )
464 : f_a( a ) , f_sh( s ) , f_clock( c ) ,
465 f_fr( a.f_options.keep_frontier ) {}
466
467 // the main loop
468 void run( unsigned i ) {
469 auto w = detail::bb_worker( f_a.f_p , i );
470 const auto & o = f_a.f_options;
471 const Selection sel = o.selection;
472 std::optional< Item > dive; // the node of the dive
473 std::vector< Item > kids; // the children of a node
474 const auto inc = [ & ] { return( f_sh.inc.value() ); };
475 const auto improve = [ & ]( Value v , const Node & n ) {
476 return( f_sh.inc.template improve< P >( v , n , o ) );
477 };
478
479 for( ;; ) {
480 std::optional< Item > it;
481 if( dive ) {
482 it.emplace( std::move( *dive ) );
483 dive.reset();
484 }
485 else
486 if( ! ( it = next() ) )
487 return;
488
489 if( stop() ) { // the node stays open
490 f_fr.add( std::move( it->node ) , it->bound , it->depth ,
492 f_sh.busy.fetch_sub( 1 );
493 return;
494 }
495
496 kids.clear();
497 detail::bb_process< P >( it->node , it->bound , it->depth , w , inc ,
498 improve , [ & ]( Node && c , Value b ,
499 unsigned d ) {
500 kids.push_back( Item{ std::move( c ) , b , d } );
501 } , o , f_st , f_fr );
502 f_sh.evaluated.fetch_add( 1 );
503
504 std::lock_guard< Lock > guard( f_sh.lock );
505 const std::size_t k = detail::bb_dive( kids , f_sh.q.best_bound() ,
506 inc() , o );
507 for( std::size_t j = 0 ; j < kids.size() ; ++j )
508 if( j == k )
509 dive.emplace( std::move( kids[ j ] ) );
510 else {
511 const Value key = detail::bb_key( f_a.f_p , kids[ j ].node ,
512 kids[ j ].bound , kids[ j ].depth ,
513 sel );
514 f_sh.q.push( std::move( kids[ j ].node ) , kids[ j ].bound ,
515 kids[ j ].depth , key );
516 }
517 f_sh.open.store( f_sh.q.size() );
518 f_sh.max_open = std::max( f_sh.max_open , f_sh.q.size() );
519 if( ! dive )
520 f_sh.busy.fetch_sub( 1 );
521 }
522 }
523
524 // the next node of the queue, counting the worker as busy; none when the
525 // search is over
526 std::optional< Item > next( void ) {
527 ADS::Backoff backoff;
528 for( ;; ) {
529 if( f_sh.done.load( std::memory_order_acquire ) )
530 return( std::nullopt );
531 if( f_sh.open.load() == 0 ) { // wait without the lock
532 if( f_sh.busy.load() == 0 ) {
533 std::lock_guard< Lock > guard( f_sh.lock );
534 if( f_sh.q.empty() && ( f_sh.busy.load() == 0 ) )
535 f_sh.done.store( true , std::memory_order_release );
536 continue;
537 }
538 backoff.pause();
539 continue;
540 }
541 std::lock_guard< Lock > guard( f_sh.lock );
542 if( f_sh.q.empty() )
543 continue;
544 const Value best = f_sh.q.best_bound();
545 if( detail::bb_prunable( best , f_sh.inc.value() , f_a.f_options ) ) {
546 f_st.pruned_extracted += f_fr.add( f_sh.q , BBLeafKind::pruned );
547 f_sh.open.store( 0 ); // nothing can improve
548 continue;
549 }
550 f_sh.busy.fetch_add( 1 );
551 Item it = f_sh.q.pop();
552 f_sh.open.store( f_sh.q.size() );
553 return( it );
554 }
555 }
556
557 // true if a limit is reached, which ends the search
558 bool stop( void ) {
559 if( ! detail::bb_limit( f_a.f_options , f_sh.evaluated.load() , f_clock ,
560 f_sh.limit , f_sh.time ) )
561 return( false );
562 f_sh.done.store( true , std::memory_order_release );
563 return( true );
564 }
565
566 const SharedBranchAndBound & f_a; // the search
567 Shared & f_sh; // the shared data
568 const Stopwatch & f_clock; // the clock of the search
569 BBStats f_st; // its counters
570 detail::BBFrontier< Node , Value > f_fr; // its leaves
571 };
572
573/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
574
575 const P & f_p;
576 Options f_options;
577 Executor * f_ex = nullptr;
578
579 }; // end( class( SharedBranchAndBound ) )
580
581/*--------------------------------------------------------------------------*/
582/*-------------------- CLASS DistributedBranchAndBound ---------------------*/
583/*--------------------------------------------------------------------------*/
586
593
594template< BranchAndBound P , ADS::WorkerExecutor Executor = ADS::ThreadPool >
596
597/*------------------------ PUBLIC PART OF THE CLASS ------------------------*/
598
599 public:
600
601/*------------------------------ PUBLIC TYPES ------------------------------*/
602
603 using Node = typename P::node_type;
608
610 struct Msg {
613 unsigned depth;
614 };
615
616/*------------------------------ CONSTRUCTOR -------------------------------*/
617
619
620 explicit DistributedBranchAndBound( const P & problem ,
621 const Options & options = {} )
622 : f_p( problem ) , f_options( options ) {
623 f_options.normalize();
624 }
625
626/*------------------------- OTHER INITIALIZATIONS --------------------------*/
630
631 void set_executor( Executor & ex ) noexcept { f_ex = &ex; }
632
633/*-------------------------------- SOLVING ---------------------------------*/
635
636 [[nodiscard]] SolutionT solve( void ) const {
637 return( run( std::nullopt ) );
638 }
639
640/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
645
646 [[nodiscard]] SolutionT solve( std::vector< Leaf > seeds ) const {
647 return( run( std::move( seeds ) ) );
648 }
649
650/*----------------------- PRIVATE PART OF THE CLASS ------------------------*/
651
652 private:
653
654 SolutionT run( std::optional< std::vector< Leaf > > seeds ) const
655 {
656 const Value inf = BBValueTraits< Value >::infinity();
657 const unsigned p = f_options.threads;
658 Stopwatch clock;
659 const Value start = f_options.incumbent ?
660 detail::bb_signed< P >( *f_options.incumbent ) : inf;
661 Shared sh( p , start , f_options.batch );
662 {
663 auto & b = sh.seeds;
664 std::size_t i = 0;
665 for( auto & l : detail::bb_starts( f_p , std::move( seeds ) ) ) {
666 unsigned dst;
667 if constexpr( HashedBranchAndBound< P > )
668 dst = unsigned( std::uint64_t( f_p.hash( l.node ) ) % p );
669 else
670 dst = unsigned( i % p );
671 ++i;
672 b[ dst ].push_back( Msg{ std::move( l.node ) , l.bound , l.depth } );
673 }
674 }
675
676 std::vector< std::unique_ptr< Worker > > w( p );
677 for( unsigned i = 0 ; i < p ; ++i )
678 w[ i ] = std::make_unique< Worker >( *this , sh , i , clock );
679 run_workers( f_ex , p , [ & ]( unsigned i ) { w[ i ]->run(); } );
680
681 // the nodes left open by a limit bound the optimum too
682 SolutionT sol;
683 detail::BBFrontier< Node , Value > fr( f_options.keep_frontier );
684 const auto open = [ & ]( Msg && m ) {
685 fr.add( std::move( m.node ) , m.bound , m.depth , BBLeafKind::open );
686 };
687 for( unsigned i = 0 ; i < p ; ++i ) {
688 Worker & x = *w[ i ];
689 x.f_st.messages = sh.mail.batches( i );
690 sol.stats.add( x.f_st );
691 fr.merge( std::move( x.f_fr ) );
692 fr.add( x.f_q , BBLeafKind::open );
693 if( x.f_dive )
694 fr.add( std::move( x.f_dive->node ) , x.f_dive->bound ,
695 x.f_dive->depth , BBLeafKind::open );
696 }
697 sh.mail.leftovers( open );
698 detail::bb_finish< P >( sol , sh.inc , fr , sh.limit.load() ,
699 sh.time.load() );
700 sol.stats.seconds = clock.seconds();
701 return( sol );
702 }
703
704/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
705
706 using Mailbox = ADS::Mailbox< Msg , ADS::DefaultChannels< Msg , Executor > >;
707 using Item = typename BBQueue< Node , Value >::Item;
708
709/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
710 // the data shared by the workers
711
712 struct Shared {
713
714 Shared( unsigned np , Value z , std::size_t batch )
715 : p( np ) , mail( np , batch ) , seeds( np ) , inc( z ) {}
716
717 unsigned p; // the number of workers
718 Mailbox mail; // the messages
719 std::vector< std::vector< Msg > > seeds; // the first nodes, by owner
720 BBIncumbent< Node , Value > inc; // the incumbent
721 alignas( cache_line ) std::atomic< std::uint64_t > evaluated{ 0 };
722 std::atomic< bool > limit{ false }; // stopped by a limit
723 std::atomic< bool > time{ false }; // ... the time limit
724 };
725
726/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
727 // a worker: its queue, its outboxes and its counters
728
729 struct alignas( cache_line ) Worker {
730
731 Worker( const DistributedBranchAndBound & a , Shared & s , unsigned i ,
732 const Stopwatch & c )
733 : f_a( a ) , f_sh( s ) , f_id( i ) , f_clock( c ) ,
734 f_q( a.f_options.selection ) ,
735 f_gen( a.f_options.seed + i ) ,
736 f_fr( a.f_options.keep_frontier ) {}
737
738 // the main loop
739 void run( void ) {
740 auto w = detail::bb_worker( f_a.f_p , f_id );
741 const auto & o = f_a.f_options;
742 const auto inc = [ & ] { return( f_sh.inc.value() ); };
743 const auto improve = [ & ]( Value v , const Node & n ) {
744 return( f_sh.inc.template improve< P >( v , n , o ) );
745 };
746 for( Msg & m : f_sh.seeds[ f_id ] )
747 push( Item{ std::move( m.node ) , m.bound , m.depth } );
748 unsigned since_flush = 0;
749 ADS::Backoff backoff;
750
751 while( ! f_sh.mail.stopped() ) {
752 f_sh.mail.poll( f_id , [ & ]( Msg && m ) {
753 push( Item{ std::move( m.node ) , m.bound , m.depth } );
754 } );
755
756 if( std::optional< Item > it = next() ) {
757 backoff.reset();
758 if( stop() ) { // the node stays open
759 f_fr.add( std::move( it->node ) , it->bound , it->depth ,
760 BBLeafKind::open );
761 return;
762 }
763 v_kids.clear();
764 detail::bb_process< P >( it->node , it->bound , it->depth , w , inc ,
765 improve , [ & ]( Node && c , Value b ,
766 unsigned d ) {
767 v_kids.push_back( Item{ std::move( c ) , b ,
768 d } );
769 } , o , f_st , f_fr );
770 f_sh.evaluated.fetch_add( 1 );
771 const std::size_t k = detail::bb_dive( v_kids , f_q.best_bound() ,
772 inc() , o );
773 for( std::size_t j = 0 ; j < v_kids.size() ; ++j )
774 if( j == k )
775 f_dive.emplace( std::move( v_kids[ j ] ) );
776 else
777 send( std::move( v_kids[ j ] ) );
778 if( ++since_flush >= f_a.f_options.flush_period ) {
779 since_flush = 0;
780 f_sh.mail.flush( f_id );
781 }
782 continue;
783 }
784
785 // nothing to evaluate: send everything, and end if no worker has work
786 // and no message is in transit
787 if( f_sh.mail.quiescent( f_id ) )
788 return;
789 backoff.pause();
790 }
791 }
792
793 // pushes a node in the queue
794 void push( Item && it ) {
795 const Value key = detail::bb_key( f_a.f_p , it.node , it.bound ,
796 it.depth , f_a.f_options.selection );
797 f_q.push( std::move( it.node ) , it.bound , it.depth , key );
798 f_st.max_open = std::max( f_st.max_open , f_q.size() );
799 }
800
801 // the next node: the dive, or the best of the queue; none if there is
802 // none, or if none can improve the incumbent (which empties the queue)
803 std::optional< Item > next( void ) {
804 if( f_dive ) {
805 std::optional< Item > it( std::move( f_dive ) );
806 f_dive.reset();
807 return( it );
808 }
809 if( f_q.empty() )
810 return( std::nullopt );
811 const Value best = f_q.best_bound();
812 if( detail::bb_prunable( best , f_sh.inc.value() , f_a.f_options ) ) {
813 f_st.pruned_extracted += f_fr.add( f_q , BBLeafKind::pruned );
814 return( std::nullopt );
815 }
816 return( f_q.pop() );
817 }
818
819 // sends a child to its owner, or keeps it if the worker owns it
820 void send( Item && it ) {
821 const unsigned p = f_sh.p;
822 unsigned dst;
823 if constexpr( HashedBranchAndBound< P > )
824 dst = unsigned( std::uint64_t( f_a.f_p.hash( it.node ) ) % p );
825 else
826 dst = unsigned( random_below( f_gen , p ) );
827 if( dst == f_id ) {
828 push( std::move( it ) );
829 return;
830 }
831 f_sh.mail.send( f_id , dst , Msg{ std::move( it.node ) , it.bound ,
832 it.depth } );
833 ++f_st.sent;
834 }
835
836 // true if a limit is reached, which ends the search
837 bool stop( void ) {
838 if( ! detail::bb_limit( f_a.f_options , f_sh.evaluated.load() , f_clock ,
839 f_sh.limit , f_sh.time ) )
840 return( false );
841 f_sh.mail.stop();
842 return( true );
843 }
844
845 const DistributedBranchAndBound & f_a; // the search
846 Shared & f_sh; // the shared data
847 unsigned f_id; // the index of the worker
848 const Stopwatch & f_clock; // the clock of the search
849 BBQueue< Node , Value > f_q; // its open nodes
850 std::optional< Item > f_dive; // the node of its dive
851 std::mt19937_64 f_gen; // the random owners
852 std::vector< Item > v_kids; // the children of a node
853 BBStats f_st; // its counters
854 detail::BBFrontier< Node , Value > f_fr; // its leaves
855 };
856
857/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
858
859 const P & f_p;
860 Options f_options;
861 Executor * f_ex = nullptr;
862
863 }; // end( class( DistributedBranchAndBound ) )
864
865/*--------------------------------------------------------------------------*/
866
867} // end( namespace PBeFS_di_unipi_it )
868
869/*--------------------------------------------------------------------------*/
870
871#endif /* __PBeFS_ParallelBranchAndBound */
872
873/*--------------------------------------------------------------------------*/
874/*------------------- End File ParallelBranchAndBound.h --------------------*/
875/*--------------------------------------------------------------------------*/
Header file for the best-first search with an incumbent, i.e., for Branch-and-Bound (B&B): the concep...
BBIncumbent(V v)
constructor, taking the initial value (infinity() of BBValueTraits if none)
Definition ParallelBranchAndBound.h:195
bool improve(V v, const Node &n, const BasicBBOptions< V > &o)
offers the value v of the node n of the problem P; true if it is a new incumbent
Definition ParallelBranchAndBound.h:214
V value(void) const
the current value
Definition ParallelBranchAndBound.h:200
std::optional< Node > take_node(void)
moves out the node of the incumbent, if any
Definition ParallelBranchAndBound.h:233
BasicParallelBBOptions< Value > Options
the options
Definition ParallelBranchAndBound.h:605
typename P::node_type Node
the node type
Definition ParallelBranchAndBound.h:603
BBLeaf< Node, Value > Leaf
the leaf type
Definition ParallelBranchAndBound.h:606
SolutionT solve(std::vector< Leaf > seeds) const
searches the subtrees of the given nodes, returning the Solution [see BranchAndBoundSearch::solve()];...
Definition ParallelBranchAndBound.h:646
DistributedBranchAndBound(const P &problem, const Options &options={})
constructor, taking the problem (which must outlive the search)
Definition ParallelBranchAndBound.h:620
SolutionT solve(void) const
searches the tree from the root, returning the Solution
Definition ParallelBranchAndBound.h:636
BBSolution< Node, Value > SolutionT
the solution type
Definition ParallelBranchAndBound.h:607
void set_executor(Executor &ex) noexcept
sets the Executor that runs the workers, which must outlive the searches; without it,...
Definition ParallelBranchAndBound.h:631
bb_value_t< P > Value
the value type
Definition ParallelBranchAndBound.h:604
BasicParallelBBOptions< Value > Options
the options
Definition ParallelBranchAndBound.h:358
typename P::node_type Node
the node type
Definition ParallelBranchAndBound.h:356
SharedBranchAndBound(const P &problem, const Options &options={})
constructor, taking the problem (which must outlive the search)
Definition ParallelBranchAndBound.h:366
BBLeaf< Node, Value > Leaf
the leaf type
Definition ParallelBranchAndBound.h:359
SolutionT solve(std::vector< Leaf > seeds) const
searches the subtrees of the given nodes, returning the Solution [see BranchAndBoundSearch::solve()]
Definition ParallelBranchAndBound.h:390
SolutionT solve(void) const
searches the tree from the root, returning the Solution
Definition ParallelBranchAndBound.h:382
BBSolution< Node, Value > SolutionT
the solution type
Definition ParallelBranchAndBound.h:360
void set_executor(Executor &ex) noexcept
sets the Executor that runs the workers, which must outlive the searches; without it,...
Definition ParallelBranchAndBound.h:377
bb_value_t< P > Value
the value type
Definition ParallelBranchAndBound.h:357
a wall-clock stopwatch, started at construction
Definition SearchUtils.h:175
double seconds(void) const
the seconds elapsed since the construction
Definition SearchUtils.h:182
a B&B problem that gives a hash of its nodes
Definition BranchAndBound.h:353
namespace for the Parallel Best-First Search library
Definition AStar.h:83
@ open
left open by a limit
Definition BranchAndBound.h:167
@ pruned
its bound cannot improve the incumbent
Definition BranchAndBound.h:168
Selection
the order in which the open nodes are extracted
Definition BranchAndBound.h:137
@ plunging
a dive into the children, then best-first
Definition BranchAndBound.h:142
@ depth_first
the deepest node first
Definition BranchAndBound.h:139
constexpr std::size_t cache_line
the size of a cache line, used to keep the data of the workers apart
Definition SearchUtils.h:66
std::uint64_t random_below(Gen &gen, std::uint64_t n)
an integer in [ 0 , n ) from the next word of a 64-bit generator, n > 0
Definition SearchUtils.h:165
typename detail::bb_value< P >::type bb_value_t
the type of the values of the B&B problem P: its value_type, or double
Definition BranchAndBound.h:293
@ optimal
the incumbent is optimal within the tolerance
Definition BranchAndBound.h:147
@ time_limit
the time limit was reached first
Definition BranchAndBound.h:150
@ node_limit
the limit on the evaluated nodes was reached first
Definition BranchAndBound.h:149
@ infeasible
no feasible solution exists (nor was given)
Definition BranchAndBound.h:148
void run_workers(Executor *ex, unsigned p, F &&f)
runs the workers f( 0 ) , ... , f( p - 1 ) at once on an Executor
Definition SearchUtils.h:121
BasicParallelBBOptions<> ParallelBBOptions
the options of the parallel B&B with values of type double
Definition ParallelBranchAndBound.h:163
a leaf of the tree explored, or a node to start a search from
Definition BranchAndBound.h:181
the outcome of a B&B
Definition BranchAndBound.h:472
static constexpr V infinity(void) noexcept
the infinite value
Definition BranchAndBound.h:212
the options of a B&B with values of type V
Definition BranchAndBound.h:383
std::optional< V > incumbent
a known feasible value, if any
Definition BranchAndBound.h:389
std::function< void(V) > on_incumbent
called with every new incumbent value (in a parallel search, by the worker that finds it,...
Definition BranchAndBound.h:402
Selection selection
the order of the nodes
Definition BranchAndBound.h:385
the options of the parallel B&B with values of type V
Definition ParallelBranchAndBound.h:135
BasicParallelBBOptions(void)
Definition ParallelBranchAndBound.h:137
std::size_t round
Definition ParallelBranchAndBound.h:144
bool split_shared
Definition ParallelBranchAndBound.h:158
bool split_dive
Definition ParallelBranchAndBound.h:153
std::size_t split
Definition ParallelBranchAndBound.h:148
a node travelling to its owner
Definition ParallelBranchAndBound.h:610
Value bound
Definition ParallelBranchAndBound.h:612
unsigned depth
Definition ParallelBranchAndBound.h:613
Node node
Definition ParallelBranchAndBound.h:611
a node of the search graph
Definition Node.h:74
the parameters shared by the parallel searches
Definition SearchUtils.h:94
std::size_t batch
nodes packed in a message
Definition SearchUtils.h:97
unsigned flush_period
steps between flushes of outboxes
Definition SearchUtils.h:98
unsigned threads
the number of workers
Definition SearchUtils.h:96
void normalize(void) noexcept
sets to 1 any parameter that is 0
Definition SearchUtils.h:102