138 using Node =
typename P::node_type;
150 : f_p( problem ) , f_options( options ) {
152 if( f_options.
split == 0 )
167 return( run( std::nullopt ) );
175 return( run( std::move( seeds ) ) );
186 SolutionT run( std::optional< std::vector< Leaf > > seeds )
const
189 const Value inf = Tr::infinity();
192 const bool sample = p > 1;
201 const auto push = [ & ]( Item && it ) {
202 const Value key = detail::bb_key( f_p , it.node , it.bound , it.depth ,
204 q.push( std::move( it.node ) , it.bound , it.depth , key );
207 for(
auto & l : detail::bb_starts( f_p , std::move( seeds ) ) )
208 push( Item{ std::move( l.node ) , l.bound , l.depth } );
210 Value inc = o.incumbent ? detail::bb_signed< P >( *o.incumbent ) : inf;
211 std::optional< Node > inc_node;
212 detail::BBFrontier< Node , Value > fr( o.keep_frontier );
213 bool limit = false , late =
false;
215 auto w = detail::bb_worker( f_p , 0u );
216 const auto improve = [ & ](
Value v ,
const Node & n ) {
222 o.on_incumbent( detail::bb_signed< P >( v ) );
225 std::vector< Item > kids;
226 while( sample && ! q.empty() && ( q.size() < o.split * p ) ) {
227 if( o.node_limit && ( st.
evaluated >= o.node_limit ) ) {
231 if( ( o.time_limit > 0 ) && ( clock.
seconds() >= o.time_limit ) ) {
235 if( detail::bb_prunable( q.best_bound() , inc , o ) ) {
241 detail::bb_process< P >( it.node , it.bound , it.depth , w ,
242 [ & ] { return( inc ); } , improve ,
243 [ & ](
Node && c ,
Value b ,
unsigned d ) {
244 kids.push_back( Item{ std::move( c ) , b ,
247 for( Item & k : kids )
248 push( std::move( k ) );
253 if( sample && o.split_dive && ! limit && ! q.empty() &&
254 ! detail::bb_prunable( q.best_bound() , inc , o ) ) {
255 detail::BBFrontier< Node , Value > none( false );
256 const Item & top = q.top();
257 Item d{ Node( top.node ) , top.bound , top.depth };
259 if( o.node_limit && ( st.evaluated >= o.node_limit ) )
261 if( ( o.time_limit > 0 ) && ( clock.seconds() >= o.time_limit ) )
264 detail::bb_process< P >( d.node , d.bound , d.depth , w ,
265 [ & ] { return( inc ); } , improve ,
266 [ & ](
Node && c ,
Value b ,
unsigned dp ) {
267 kids.push_back( Item{ std::move( c ) , b ,
274 for( std::size_t j = 0 ; j < kids.size() ; ++j ) {
275 const Value e = detail::bb_key( f_p , kids[ j ].node , kids[ j ].bound ,
278 if( ( j == 0 ) || ( e < best ) ) {
283 d = std::move( kids[ c ] );
290 std::vector< std::vector< Leaf > > part( p );
291 if( ! limit && ! q.empty() &&
292 detail::bb_prunable( q.best_bound() , inc , o ) )
293 st.pruned_extracted += fr.add( q , BBLeafKind::pruned );
294 std::uint64_t rest = 0;
296 if( o.node_limit && ! limit )
297 rest = o.node_limit - st.evaluated;
298 if( ( o.time_limit > 0 ) && ! limit ) {
299 time = o.time_limit - clock.
seconds();
304 for( std::size_t k = 0 ; ! q.empty() ; ++k ) {
306 const unsigned i = unsigned( k % p );
307 if( o.node_limit && ( share( rest , p , i ) == 0 ) ) {
309 fr.add( std::move( it.node ) , it.bound , it.depth , BBLeafKind::open );
312 part[ i ].push_back( Leaf{ std::move( it.node ) ,
313 detail::bb_signed< P >( it.bound ) ,
314 it.depth , BBLeafKind::open } );
316 fr.add( q , BBLeafKind::open );
317 st.evaluated_by.assign( p , 0 );
318 st.evaluated_by[ 0 ] = st.evaluated;
323 std::vector< std::optional< SolutionT > > out( p );
325 BBIncumbent< std::monostate , Value > best( inc );
326 std::exception_ptr error;
328 if( part[ i ].empty() )
331 BasicBBOptions< Value > wo = o;
332 if( Tr::finite( inc ) )
333 wo.incumbent = detail::bb_signed< P >( inc );
334 wo.node_limit = o.node_limit ? share( rest , p , i ) : 0;
335 wo.time_limit = time;
336 wo.on_incumbent = [ & ]( Value v ) {
337 ( void ) best.template improve< P >( detail::bb_signed< P >( v ) ,
338 std::monostate() , o );
341 wo.shared_incumbent = [ & ] {
342 return( detail::bb_signed< P >( best.value() ) );
344 out[ i ] = BranchAndBoundSearch< P >( f_p , wo , i ).solve(
345 std::move( part[ i ] ) );
348 std::lock_guard< std::mutex > guard( m );
350 error = std::current_exception();
354 std::rethrow_exception( error );
357 Value low = std::min( inc , fr.low );
358 fr.template take< P >( sol.frontier );
359 for(
unsigned i = 0 ; i < p ; ++i ) {
362 SolutionT & s = *out[ i ];
363 if( s.node && ( detail::bb_signed< P >( s.value ) < inc ) ) {
364 inc = detail::bb_signed< P >( s.value );
365 inc_node = std::move( s.node );
367 low = std::min( low , detail::bb_signed< P >( s.bound ) );
368 if( s.status == BBStatus::time_limit )
371 if( s.status == BBStatus::node_limit )
374 st.evaluated_by.pop_back();
375 st.evaluated_by[ i ] += s.stats.evaluated;
376 sol.frontier.insert( sol.frontier.end() ,
377 std::make_move_iterator( s.frontier.begin() ) ,
378 std::make_move_iterator( s.frontier.end() ) );
382 sol.status = late ? BBStatus::time_limit : BBStatus::node_limit;
384 sol.status = Tr::finite( inc ) ? BBStatus::optimal
385 : BBStatus::infeasible;
386 sol.node = std::move( inc_node );
387 if( Tr::finite( inc ) )
388 sol.value = detail::bb_signed< P >( inc );
389 low = std::min( low , inc );
390 sol.bound = detail::bb_signed< P >( low );
391 sol.gap = Tr::finite( inc ) && Tr::finite( low ) ? Value( inc - low )
400 static std::uint64_t share( std::uint64_t rest ,
unsigned p ,
unsigned i )
402 return( rest / p + ( i < rest % p ? 1 : 0 ) );
409 Executor * f_ex =
nullptr;