125 using Node =
typename P::node_type;
137 : f_p( problem ) , f_options( options ) {
152 return( run( std::nullopt ) );
161 return( run( std::move( seeds ) ) );
177 using Order = std::pair< Value , std::uint64_t >;
179 using Frontier = ADS::StealingFrontier< Order , Held , std::less<> ,
Value >;
180 using Item =
typename BBQueue< Node , Value >::Item;
184 SolutionT run( std::optional< std::vector< Leaf > > seeds )
const
187 const unsigned p = f_options.
threads;
190 detail::bb_signed< P >( *f_options.
incumbent ) : inf;
191 Shared sh( p , start , f_options.
seed );
193 std::vector< std::unique_ptr< Worker > > w( p );
194 for(
unsigned i = 0 ; i < p ; ++i )
195 w[ i ] = std::make_unique< Worker >( *
this , sh , i , clock );
197 for(
auto & l : detail::bb_starts( f_p , std::move( seeds ) ) )
198 w[ n++ % p ]->push( Item{ std::move( l.node ) , l.bound , l.depth } );
200 const auto body = [ & ]( std::size_t i ) {
201 if( ! ADS::isRunCaller() )
206 sh.frontier.run( *f_ex , body );
209 sh.frontier.run( ex , body );
214 detail::BBFrontier< Node , Value > fr( f_options.keep_frontier );
215 for(
auto & x : w ) {
216 sol.stats.add( x->f_st );
217 fr.merge( std::move( x->f_fr ) );
219 fr.add( std::move( x->f_dive->node ) , x->f_dive->bound ,
222 sh.frontier.leftovers( [ & ](
auto && e ) {
223 fr.add( std::move( e.item.node ) , e.bound , e.item.depth ,
226 sol.stats.sent = sh.frontier.steals();
227 detail::bb_finish< P >( sol , sh.inc , fr , sh.limit.load() ,
229 sol.stats.seconds = clock.seconds();
238 Shared(
unsigned np ,
Value z , std::uint64_t seed )
239 : frontier( np , seed ) , inc( z ) {}
242 BBIncumbent< Node , Value > inc;
243 alignas(
cache_line ) std::atomic< std::uint64_t > evaluated{ 0 };
244 std::atomic< bool > limit{
false };
245 std::atomic< bool > time{
false };
252 struct alignas( cache_line ) Worker {
255 const Stopwatch & c )
256 : f_a( a ) , f_sh( s ) , f_id( i ) , f_clock( c ) ,
258 f_fr( a.f_options.keep_frontier ) {}
262 auto w = detail::bb_worker( f_a.f_p , f_id );
263 const auto & o = f_a.f_options;
264 const auto inc = [ & ] {
return( f_sh.inc.value() ); };
265 const auto improve = [ & ](
Value v ,
const Node & n ) {
266 return( f_sh.inc.template improve< P >( v , n , o ) );
270 std::optional< Item > it = next();
274 f_fr.add( std::move( it->node ) , it->bound , it->depth ,
280 detail::bb_process< P >( it->node , it->bound , it->depth , w , inc ,
283 v_kids.push_back( Item{ std::move( c ) , b ,
285 } , o , f_st , f_fr );
286 f_sh.evaluated.fetch_add( 1 );
288 const std::size_t k = detail::bb_dive( v_kids , best_bound() , inc() ,
290 for( std::size_t j = 0 ; j < v_kids.size() ; ++j )
292 f_dive.emplace( std::move( v_kids[ j ] ) );
294 push( std::move( v_kids[ j ] ) );
299 void push( Item && it ) {
300 const Value key = detail::bb_key( f_a.f_p , it.node , it.bound ,
301 it.depth , f_a.f_options.selection );
302 const std::uint64_t seq = ++f_seq;
303 f_sh.frontier.push( f_id , Order( key , f_fifo ? seq : ~seq ) , it.bound ,
304 Held{ std::move( it.node ) , it.depth } );
305 f_st.max_open = std::max( f_st.max_open ,
306 f_sh.frontier.queue( f_id ).size() );
311 [[nodiscard]]
Value best_bound(
void )
const {
312 const auto b = f_sh.frontier.minBound( f_id );
320 std::optional< Item > next(
void ) {
322 std::optional< Item > it( std::move( f_dive ) );
326 if(
const auto b = f_sh.frontier.minBound( f_id ) ;
327 b && detail::bb_prunable( *b , f_sh.inc.value() , f_a.f_options ) )
328 f_st.pruned_extracted += f_sh.frontier.drain( f_id , [ & ](
auto && e ) {
329 f_fr.add( std::move( e.item.node ) , e.bound , e.item.depth ,
332 auto e = f_sh.frontier.pop( f_id );
334 return( std::nullopt );
335 return( Item{ std::move( e->item.node ) , e->bound , e->item.depth } );
340 if( ! detail::bb_limit( f_a.f_options , f_sh.evaluated.load() , f_clock ,
341 f_sh.limit , f_sh.time ) )
343 f_sh.frontier.stop();
350 const Stopwatch & f_clock;
352 std::uint64_t f_seq = 0;
353 std::optional< Item > f_dive;
354 std::vector< Item > v_kids;
356 detail::BBFrontier< Node , Value > f_fr;
363 Executor * f_ex =
nullptr;