PBeFS
Parallel Best-First Search
Loading...
Searching...
No Matches
BranchAndBound.h
Go to the documentation of this file.
1/*--------------------------------------------------------------------------*/
2/*------------------------- File BranchAndBound.h --------------------------*/
3/*--------------------------------------------------------------------------*/
80/*--------------------------------------------------------------------------*/
81/*------------------------------ DEFINITIONS -------------------------------*/
82/*--------------------------------------------------------------------------*/
83
84#ifndef __PBeFS_BranchAndBound
85 #define __PBeFS_BranchAndBound
86 /* self-identification: #endif at the end of the file */
87
88/*--------------------------------------------------------------------------*/
89/*-------------------------------- INCLUDES --------------------------------*/
90/*--------------------------------------------------------------------------*/
91
92#include <algorithm>
93
94#include <cmath>
95
96#include <concepts>
97
98#include <cstddef>
99
100#include <cstdint>
101
102#include <functional>
103
104#include <limits>
105
106#include <optional>
107
108#include <type_traits>
109
110#include <utility>
111
112#include <vector>
113
115
116#include "ADS/priority/IndexedHeapPair.h"
117
118/*--------------------------------------------------------------------------*/
119/*------------------------------- NAMESPACE --------------------------------*/
120/*--------------------------------------------------------------------------*/
121
123namespace PBeFS_di_unipi_it
124{
125
126/*--------------------------------------------------------------------------*/
127/*--------------------------------- TYPES ----------------------------------*/
128/*--------------------------------------------------------------------------*/
129
131enum class Sense {
134 };
135
144
152
154template< class V = double >
156 V bound = V( 0 );
157 bool infeasible = false;
158 std::optional< V > feasible;
160 };
161
164
172
174
179
180template< class Node , class V = double >
187
188/*--------------------------------------------------------------------------*/
189/*-------------------------- CLASS BBValueTraits ---------------------------*/
190/*--------------------------------------------------------------------------*/
192
203
204template< class V >
206
207 static_assert( std::is_arithmetic_v< V > && std::is_signed_v< V > ,
208 "BBValueTraits: specialize it for a type that is not a "
209 "signed arithmetic one" );
210
212 [[nodiscard]] static constexpr V infinity( void ) noexcept {
213 if constexpr( std::numeric_limits< V >::has_infinity )
214 return( std::numeric_limits< V >::infinity() );
215 else
216 return( std::numeric_limits< V >::max() );
217 }
218
220 [[nodiscard]] static constexpr V none( void ) noexcept {
221 if constexpr( std::numeric_limits< V >::has_quiet_NaN )
222 return( std::numeric_limits< V >::quiet_NaN() );
223 else
224 return( infinity() );
225 }
226
228 [[nodiscard]] static constexpr bool finite( V v ) noexcept {
229 return( ( v > - infinity() ) && ( v < infinity() ) );
230 }
231
234 [[nodiscard]] static V tolerance( V z , V a , double r ) noexcept {
235 if constexpr( std::is_floating_point_v< V > )
236 return( std::max( a , V( r * std::abs( z ) ) ) );
237 else {
238 using L = long double;
239 return( std::max( a , V( std::floor( L( r ) * std::abs( L( z ) ) ) ) ) );
240 }
241 }
242
244 [[nodiscard]] static bool within( V b , V l , V z , double g ) noexcept {
245 if( ! finite( l ) )
246 return( false );
247 if constexpr( std::is_floating_point_v< V > )
248 return( b <= l + g * ( z - l ) );
249 else {
250 using L = long double;
251 return( L( b ) <= L( l ) + L( g ) * ( L( z ) - L( l ) ) );
252 }
253 }
254
255 }; // end( struct( BBValueTraits ) )
256
257/*--------------------------------------------------------------------------*/
260
261template< class V >
262concept BBValue = std::totally_ordered< V > && std::copyable< V > &&
263 std::constructible_from< V , int > &&
264 requires( const V & a , double r ) {
265 { - a } -> std::convertible_to< V >;
266 { a - a } -> std::convertible_to< V >;
267 { BBValueTraits< V >::infinity() } -> std::convertible_to< V >;
268 { BBValueTraits< V >::none() } -> std::convertible_to< V >;
269 { BBValueTraits< V >::finite( a ) } -> std::convertible_to< bool >;
270 { BBValueTraits< V >::tolerance( a , a , r ) } -> std::convertible_to< V >;
271 { BBValueTraits< V >::within( a , a , a , r ) }
272 -> std::convertible_to< bool >;
273 };
274
275namespace detail {
278 template< class N , class V >
279 struct bb_sink {
280 void operator()( const N & , V ) const noexcept {}
281 };
282
284 template< class P >
285 struct bb_value { using type = double; };
286
287 template< class P > requires requires { typename P::value_type; }
288 struct bb_value< P > { using type = typename P::value_type; };
289 } // end( namespace detail )
290
292template< class P >
293using bb_value_t = typename detail::bb_value< P >::type;
294
295/*--------------------------------------------------------------------------*/
296/*-------------------------------- CONCEPTS --------------------------------*/
297/*--------------------------------------------------------------------------*/
299
313
314template< class W , class N , class V = double >
315concept BBEvaluator = requires( W & w , N & n , const N & cn ) {
316 { w.evaluate( n ) } -> std::convertible_to< BasicEvaluation< V > >;
317 w.branch( cn , detail::bb_sink< N , V >{} );
318 };
319
320/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
322
335
336template< class P >
337concept BranchAndBound = requires( const P & p , unsigned i ) {
338 typename P::node_type;
339 requires std::copyable< typename P::node_type >;
340 requires BBValue< bb_value_t< P > >;
341 { P::sense } -> std::convertible_to< Sense >;
342 { p.root() } -> std::convertible_to< typename P::node_type >;
344 requires( const P & p , unsigned i ) {
345 { p.make_worker( i ) }
346 -> BBEvaluator< typename P::node_type , bb_value_t< P > >;
347 } );
348
349/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
351
352template< class P >
354 requires( const P & p , const typename P::node_type & n ) {
355 { p.hash( n ) } -> std::convertible_to< std::uint64_t >;
356 };
357
358/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
360
361template< class P >
363 requires( const P & p , const typename P::node_type & n ) {
364 { p.estimate( n ) } -> std::convertible_to< bb_value_t< P > >;
365 };
366
367/*--------------------------------------------------------------------------*/
368/*---------------------------- CLASS BBOptions -----------------------------*/
369/*--------------------------------------------------------------------------*/
371
381
382template< class V = double >
384
386 V abs_gap = V( 0 );
387 double rel_gap = 0;
388 double plunge_gap = 0.5;
389 std::optional< V > incumbent;
396 std::function< V( void ) > shared_incumbent;
397 std::size_t node_limit = 0;
398 double time_limit = 0;
399 bool keep_frontier = false;
402 std::function< void( V ) > on_incumbent;
403
404 }; // end( struct( BasicBBOptions ) )
405
408
409/*--------------------------------------------------------------------------*/
410/*----------------------------- CLASS BBStats ------------------------------*/
411/*--------------------------------------------------------------------------*/
413
416
417struct BBStats {
418
419 std::uint64_t evaluated = 0;
420 std::uint64_t branched = 0;
421 std::uint64_t generated = 0;
422 std::uint64_t infeasible = 0;
423 std::uint64_t pruned_generated = 0;
424 std::uint64_t pruned_extracted = 0;
425 std::uint64_t pruned_evaluated = 0;
426 std::uint64_t incumbents = 0;
427 std::uint64_t sent = 0;
428 std::uint64_t messages = 0;
429 std::size_t max_open = 0;
430 double seconds = 0;
431 std::vector< std::uint64_t > evaluated_by;
432
433/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
435
436 void add( const BBStats & o ) {
437 evaluated += o.evaluated;
438 branched += o.branched;
439 generated += o.generated;
445 sent += o.sent;
446 messages += o.messages;
447 max_open = std::max( max_open , o.max_open );
448 evaluated_by.push_back( o.evaluated );
449 }
450
451 }; // end( struct( BBStats ) )
452
453/*--------------------------------------------------------------------------*/
454/*---------------------------- CLASS BBSolution ----------------------------*/
455/*--------------------------------------------------------------------------*/
457
470
471template< class Node , class V = double >
473
475 std::optional< Node > node;
480 std::vector< BBLeaf< Node , V > > frontier;
481
483 [[nodiscard]] bool feasible( void ) const noexcept {
485 }
486
487 }; // end( struct( BBSolution ) )
488
489/*--------------------------------------------------------------------------*/
490/*----------------------------- CLASS BBQueue ------------------------------*/
491/*--------------------------------------------------------------------------*/
493
499
500template< class Node , class V = double >
501class BBQueue {
502
503/*------------------------ PUBLIC PART OF THE CLASS ------------------------*/
504
505 public:
506
508 struct Item {
511 unsigned depth;
512 };
513
514/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
516
518 : f_fifo( s == Selection::breadth_first ) {}
519
520/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
522
523 void push( Node && n , V bound , unsigned depth , V key ) {
524 std::size_t i;
525 if( v_free.empty() ) {
526 i = v_slot.size();
527 v_slot.emplace_back();
528 if( i >= std::size_t( f_q.ids() ) )
529 grow( 2 * i + 16 );
530 }
531 else {
532 i = v_free.back();
533 v_free.pop_back();
534 }
535 const std::uint64_t seq = ++f_seq;
536 v_slot[ i ].emplace( Item{ std::move( n ) , bound , depth } );
537 f_q.push( int( i ) , Order( key , f_fifo ? seq : ~seq ) , bound );
538 }
539
540/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
542
543 [[nodiscard]] const Item & top( void ) const {
544 return( *v_slot[ std::size_t( f_q.top() ) ] );
545 }
546
547/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
549
550 Item pop( void ) {
551 const auto i = std::size_t( f_q.pop() );
552 Item it = std::move( *v_slot[ i ] );
553 v_slot[ i ].reset();
554 v_free.push_back( i );
555 return( it );
556 }
557
558/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
560
561 [[nodiscard]] V best_bound( void ) const {
562 return( f_q.empty() ? BBValueTraits< V >::infinity() : f_q.minBound() );
563 }
564
565/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
567
568 std::size_t clear( void ) {
569 const std::size_t n = f_q.size();
570 v_slot.clear();
571 v_free.clear();
572 f_q.clear();
573 return( n );
574 }
575
576/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
579
580 template< class F >
581 std::size_t drain( F && f ) {
582 for( auto & x : v_slot )
583 if( x )
584 f( std::move( *x ) );
585 return( clear() );
586 }
587
588/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
590
591 [[nodiscard]] bool empty( void ) const noexcept { return( f_q.empty() ); }
592
593/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
595
596 [[nodiscard]] std::size_t size( void ) const noexcept {
597 return( f_q.size() );
598 }
599
600/*----------------------- PRIVATE PART OF THE CLASS ------------------------*/
601
602 private:
603
606 using Order = std::pair< V , std::uint64_t >;
607
609 using Queue = ADS::IndexedHeapPair< Order , V >;
610
611 // the heaps on n slots, with the nodes they hold
612 void grow( std::size_t n ) {
613 Queue q{ int( n ) };
614 for( std::size_t j = 0 ; j < v_slot.size() ; ++j )
615 if( v_slot[ j ] && f_q.contains( int( j ) ) )
616 q.push( int( j ) , f_q.key( int( j ) ) , f_q.bound( int( j ) ) );
617 f_q = std::move( q );
618 }
619
620/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
621
622 bool f_fifo;
623 std::uint64_t f_seq = 0;
624 std::vector< std::optional< Item > > v_slot;
625 std::vector< std::size_t > v_free;
626 Queue f_q;
627
628 }; // end( class( BBQueue ) )
629
630/*--------------------------------------------------------------------------*/
631/*------------------------------- FUNCTIONS --------------------------------*/
632/*--------------------------------------------------------------------------*/
633
634namespace detail {
635
637
638template< class P >
639struct BBSelf {
640 const P & p;
641 BasicEvaluation< bb_value_t< P > > evaluate( typename P::node_type & n ) {
642 return( p.evaluate( n ) );
643 }
644 template< class F >
645 void branch( const typename P::node_type & n , F && f ) {
646 p.branch( n , std::forward< F >( f ) );
647 }
648 };
649
650/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
652
653template< BranchAndBound P >
654auto bb_worker( const P & p , unsigned i ) {
655 if constexpr( requires { p.make_worker( i ); } )
656 return( p.make_worker( i ) );
657 else {
658 ( void ) i;
659 return( BBSelf< P >{ p } );
660 }
661 }
662
663/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
666
667template< BranchAndBound P , class V >
668constexpr V bb_signed( const V & v ) {
669 if( P::sense == Sense::minimize )
670 return( v );
671 return( - v );
672 }
673
674/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
677
678template< class V >
679bool bb_prunable( const V & bound , const V & inc ,
680 const BasicBBOptions< V > & o )
681{
682 using Tr = BBValueTraits< V >;
683 if( ! Tr::finite( inc ) )
684 return( bound == Tr::infinity() );
685 return( bound >= inc - Tr::tolerance( inc , o.abs_gap , o.rel_gap ) );
686 }
687
688/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
690
694
695template< class Node , class V = double >
696struct BBFrontier {
697
698 explicit BBFrontier( bool k = false ) : keep( k ) {}
699
702 void add( Node && n , V b , unsigned depth , BBLeafKind kind ,
703 bool lower = true ) {
704 if( lower )
705 low = std::min( low , b );
706 if( keep )
707 leaves.push_back( BBLeaf< Node , V >{ std::move( n ) , b , depth ,
708 kind } );
709 }
710
713 std::size_t add( BBQueue< Node , V > & q , BBLeafKind kind ) {
714 low = std::min( low , q.best_bound() );
715 if( ! keep )
716 return( q.clear() );
717 return( q.drain( [ & ]( auto && it ) {
718 leaves.push_back( BBLeaf< Node , V >{ std::move( it.node ) , it.bound ,
719 it.depth , kind } );
720 } ) );
721 }
722
724 void merge( BBFrontier && o ) {
725 low = std::min( low , o.low );
726 leaves.insert( leaves.end() , std::make_move_iterator( o.leaves.begin() ) ,
727 std::make_move_iterator( o.leaves.end() ) );
728 o.leaves.clear();
729 }
730
732 template< class P >
733 void take( std::vector< BBLeaf< Node , V > > & v ) {
734 for( auto & l : leaves )
735 l.bound = bb_signed< P >( l.bound );
736 v = std::move( leaves );
737 leaves.clear();
738 }
739
740 bool keep;
742 std::vector< BBLeaf< Node , V > > leaves;
743
744 }; // end( struct( BBFrontier ) )
745
746/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
749
750template< BranchAndBound P , class Seeds >
751std::vector< BBLeaf< typename P::node_type , bb_value_t< P > > > bb_starts(
752 const P & p ,
753 Seeds && seeds )
754{
755 using Node = typename P::node_type;
756 using V = bb_value_t< P >;
757 std::vector< BBLeaf< Node , V > > v;
758 if( ! seeds )
759 v.push_back( BBLeaf< Node , V >{ p.root() ,
761 0 , BBLeafKind::open } );
762 else {
763 v = std::move( *seeds );
764 for( auto & l : v )
765 l.bound = bb_signed< P >( l.bound );
766 }
767 return( v );
768 }
769
770/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
772
773template< BranchAndBound P >
774bb_value_t< P > bb_key( const P & p , const typename P::node_type & n ,
775 const bb_value_t< P > & bound ,
776 unsigned depth , Selection s )
777{
778 switch( s ) {
780 return( - bb_value_t< P >( int( depth ) ) );
782 return( bb_value_t< P >( int( depth ) ) );
784 if constexpr( EstimatingBranchAndBound< P > )
785 return( bb_signed< P >( bb_value_t< P >( p.estimate( n ) ) ) );
786 else
787 return( bound );
788 default:
789 return( bound );
790 }
791 }
792
793/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
797
801
802template< BranchAndBound P , class W , class Inc , class Improve ,
803 class Push >
804void bb_process( typename P::node_type & n , bb_value_t< P > bound ,
805 unsigned depth ,
806 W & w , Inc && inc , Improve && improve , Push && push ,
807 const BasicBBOptions< bb_value_t< P > > & o , BBStats & st ,
808 BBFrontier< typename P::node_type , bb_value_t< P > > & fr )
809{
810 using Node = typename P::node_type;
811 using V = bb_value_t< P >;
812 const V inf = BBValueTraits< V >::infinity();
813 if( bb_prunable( bound , inc() , o ) ) {
814 ++st.pruned_extracted;
815 fr.add( std::move( n ) , bound , depth , BBLeafKind::pruned );
816 return;
817 }
818 const auto cutoff = [ & ]( void ) {
819 if constexpr( requires { w.cutoff( inc() ); } )
820 w.cutoff( bb_signed< P >( inc() ) );
821 };
822 cutoff();
823 const BasicEvaluation< V > e = w.evaluate( n );
824 ++st.evaluated;
825 if( e.infeasible ) {
826 ++st.infeasible;
827 fr.add( std::move( n ) , inf , depth , BBLeafKind::infeasible , false );
828 return;
829 }
830 const V b = std::max( bound , bb_signed< P >( e.bound ) );
831 if( e.feasible && improve( bb_signed< P >( *e.feasible ) , n ) )
832 ++st.incumbents;
833 const BBLeafKind kind = e.feasible ? BBLeafKind::feasible
835 if( bb_prunable( b , inc() , o ) ) {
836 ++st.pruned_evaluated;
837 fr.add( std::move( n ) , b , depth , kind );
838 return;
839 }
840 ++st.branched;
841 const std::uint64_t g = st.generated;
842 cutoff();
843 w.branch( std::as_const( n ) , [ & ]( auto && c , V est ) {
844 ++st.generated;
845 const V cb = std::max( b , bb_signed< P >( est ) );
846 if( bb_prunable( cb , inc() , o ) ) {
847 ++st.pruned_generated;
848 if( fr.keep )
849 fr.add( Node( std::forward< decltype( c ) >( c ) ) , cb , depth + 1 ,
851 else
852 fr.low = std::min( fr.low , cb );
853 return;
854 }
855 push( Node( std::forward< decltype( c ) >( c ) ) , cb , depth + 1 );
856 } );
857 if( st.generated == g ) // no children: its subtree is n
858 fr.add( std::move( n ) , b , depth , kind , false );
859 }
860
861} // end( namespace detail )
862
863/*--------------------------------------------------------------------------*/
864/*----------------------- CLASS BranchAndBoundSearch -----------------------*/
865/*--------------------------------------------------------------------------*/
867
873
874template< BranchAndBound P >
876
877/*------------------------ PUBLIC PART OF THE CLASS ------------------------*/
878
879 public:
880
881/*------------------------------ PUBLIC TYPES ------------------------------*/
882
883 using Node = typename P::node_type;
888
889/*------------------------------ CONSTRUCTOR -------------------------------*/
890
894
895 explicit BranchAndBoundSearch( const P & problem ,
896 const Options & options = {} ,
897 unsigned worker = 0 )
898 : f_p( problem ) , f_options( options ) , f_worker( worker ) {}
899
900/*-------------------------------- SOLVING ---------------------------------*/
902
903 [[nodiscard]] SolutionT solve( void ) const {
904 return( run( std::nullopt ) );
905 }
906
907/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
909
916
917 [[nodiscard]] SolutionT solve( std::vector< Leaf > seeds ) const {
918 return( run( std::move( seeds ) ) );
919 }
920
921/*----------------------- PRIVATE PART OF THE CLASS ------------------------*/
922
923 private:
924
925 SolutionT run( std::optional< std::vector< Leaf > > seeds ) const
926 {
927 using Tr = BBValueTraits< Value >;
928 const Value inf = Tr::infinity();
929 const Options & o = f_options;
930 const Selection sel = o.selection;
931 Stopwatch clock;
932 SolutionT sol;
933 BBStats & st = sol.stats;
934 auto w = detail::bb_worker( f_p , f_worker );
935
936 Value inc = o.incumbent ? detail::bb_signed< P >( *o.incumbent ) : inf;
937 Value cut = inc; // what prunes: inc, or shared_incumbent if less
938 std::optional< Node > inc_node;
939 const auto improve = [ & ]( Value v , const Node & n ) {
940 if( v >= cut )
941 return( false );
942 inc = cut = v;
943 inc_node = n;
944 if( o.on_incumbent )
945 o.on_incumbent( detail::bb_signed< P >( v ) );
946 return( true );
947 };
948
949 using Item = typename BBQueue< Node , Value >::Item;
950 BBQueue< Node , Value > q( sel );
951 std::optional< Item > dive; // the node of the dive, if any
952 std::vector< Item > kids; // the children of a node
953 detail::BBFrontier< Node , Value > fr( o.keep_frontier ); // the leaves
954
955 const auto push = [ & ]( Item && it ) {
956 const Value key = detail::bb_key( f_p , it.node , it.bound , it.depth ,
957 sel );
958 q.push( std::move( it.node ) , it.bound , it.depth , key );
959 st.max_open = std::max( st.max_open , q.size() );
960 };
961
962 for( auto & l : detail::bb_starts( f_p , std::move( seeds ) ) )
963 push( Item{ std::move( l.node ) , l.bound , l.depth } );
964 sol.status = BBStatus::optimal;
965
966 for( ;; ) {
967 if( o.shared_incumbent )
968 cut = std::min( cut , detail::bb_signed< P >( o.shared_incumbent() ) );
969 if( o.node_limit && ( st.evaluated >= o.node_limit ) ) {
970 sol.status = BBStatus::node_limit;
971 break;
972 }
973 if( ( o.time_limit > 0 ) && ( clock.seconds() >= o.time_limit ) ) {
974 sol.status = BBStatus::time_limit;
975 break;
976 }
977 const Value best = std::min( q.best_bound() ,
978 dive ? dive->bound : inf );
979 if( q.empty() && ! dive )
980 break;
981 if( detail::bb_prunable( best , cut , o ) ) { // nothing can improve
982 st.pruned_extracted += fr.add( q , BBLeafKind::pruned );
983 if( dive ) {
984 ++st.pruned_extracted;
985 fr.add( std::move( dive->node ) , dive->bound , dive->depth ,
986 BBLeafKind::pruned );
987 dive.reset();
988 }
989 break;
990 }
991
992 Item it = dive ? std::move( *dive ) : q.pop();
993 dive.reset();
994 kids.clear();
995 detail::bb_process< P >( it.node , it.bound , it.depth , w ,
996 [ & ] { return( cut ); } , improve ,
997 [ & ]( Node && c , Value b , unsigned d ) {
998 kids.push_back( Item{ std::move( c ) , b , d } );
999 } , o , st , fr );
1000 if( kids.empty() )
1001 continue;
1002
1003 // with plunging, the best child dives if it is close to the best bound
1004 std::size_t k = kids.size();
1005 if( sel == Selection::plunging ) {
1006 std::size_t c = 0;
1007 for( std::size_t j = 1 ; j < kids.size() ; ++j )
1008 if( kids[ j ].bound <= kids[ c ].bound )
1009 c = j;
1010 const Value b0 = std::min( q.best_bound() , kids[ c ].bound );
1011 if( ! Tr::finite( cut ) ||
1012 Tr::within( kids[ c ].bound , b0 , cut , o.plunge_gap ) )
1013 k = c;
1014 }
1015 for( std::size_t j = 0 ; j < kids.size() ; ++j )
1016 if( j == k )
1017 dive.emplace( std::move( kids[ j ] ) );
1018 else
1019 push( std::move( kids[ j ] ) );
1020 }
1021
1022 // the open nodes left by a limit bound the optimum too
1023 fr.add( q , BBLeafKind::open );
1024 if( dive )
1025 fr.add( std::move( dive->node ) , dive->bound , dive->depth ,
1026 BBLeafKind::open );
1027 if( ( sol.status == BBStatus::optimal ) && ! Tr::finite( inc ) )
1028 sol.status = BBStatus::infeasible;
1029 sol.node = std::move( inc_node );
1030 if( Tr::finite( inc ) )
1031 sol.value = detail::bb_signed< P >( inc );
1032 const Value lb = std::min( inc , fr.low );
1033 sol.bound = detail::bb_signed< P >( lb );
1034 sol.gap = Tr::finite( inc ) && Tr::finite( lb ) ? Value( inc - lb ) : inf;
1035 fr.template take< P >( sol.frontier );
1036 st.evaluated_by = { st.evaluated };
1037 st.seconds = clock.seconds();
1038 return( sol );
1039 }
1040
1041/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
1042
1043 const P & f_p;
1044 Options f_options;
1045 unsigned f_worker;
1046
1047 }; // end( class( BranchAndBoundSearch ) )
1048
1049/*--------------------------------------------------------------------------*/
1050/*------------------------------- FUNCTIONS --------------------------------*/
1051/*--------------------------------------------------------------------------*/
1053
1054template< BranchAndBound P >
1055[[nodiscard]] BBSolution< typename P::node_type , bb_value_t< P > >
1056 branch_and_bound( const P & problem ,
1057 const BasicBBOptions< bb_value_t< P > > & o = {} )
1058{
1059 return( BranchAndBoundSearch< P >( problem , o ).solve() );
1060 }
1061
1062/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
1065
1066template< BranchAndBound P >
1067[[nodiscard]] BBSolution< typename P::node_type , bb_value_t< P > >
1068 branch_and_bound( const P & problem ,
1069 std::vector< BBLeaf< typename P::node_type ,
1070 bb_value_t< P > > > seeds ,
1071 const BasicBBOptions< bb_value_t< P > > & o = {} )
1072{
1073 return( BranchAndBoundSearch< P >( problem , o ).solve(
1074 std::move( seeds ) ) );
1075 }
1076
1077/*--------------------------------------------------------------------------*/
1078
1079} // end( namespace PBeFS_di_unipi_it )
1080
1081/*--------------------------------------------------------------------------*/
1082
1083#endif /* __PBeFS_BranchAndBound */
1084
1085/*--------------------------------------------------------------------------*/
1086/*----------------------- End File BranchAndBound.h ------------------------*/
1087/*--------------------------------------------------------------------------*/
Header file for the small helpers shared by the searches: a stopwatch, the placement of the workers o...
Item pop(void)
removes and returns the next node
Definition BranchAndBound.h:550
std::size_t drain(F &&f)
removes all the nodes, giving each to f( item ), and returns how many there were
Definition BranchAndBound.h:581
V best_bound(void) const
the least bound of the open nodes ( if there is none)
Definition BranchAndBound.h:561
BBQueue(Selection s=Selection::best_first)
constructor, taking the selection the keys are made by
Definition BranchAndBound.h:517
std::size_t size(void) const noexcept
the number of open nodes
Definition BranchAndBound.h:596
void push(Node &&n, V bound, unsigned depth, V key)
adds a node with its bound, its depth and its key
Definition BranchAndBound.h:523
std::size_t clear(void)
removes all the nodes, returning how many there were
Definition BranchAndBound.h:568
bool empty(void) const noexcept
true if there is no open node
Definition BranchAndBound.h:591
const Item & top(void) const
the next node, which stays in the queue (there must be one)
Definition BranchAndBound.h:543
typename P::node_type Node
the node type
Definition BranchAndBound.h:883
BBLeaf< Node, Value > Leaf
the leaf type
Definition BranchAndBound.h:886
BasicBBOptions< Value > Options
the options type
Definition BranchAndBound.h:885
BranchAndBoundSearch(const P &problem, const Options &options={}, unsigned worker=0)
constructor, taking the problem (which must outlive the search) and the worker whose evaluator the se...
Definition BranchAndBound.h:895
SolutionT solve(std::vector< Leaf > seeds) const
searches the subtrees of the given nodes, returning the Solution
Definition BranchAndBound.h:917
SolutionT solve(void) const
searches the tree from the root, returning the Solution
Definition BranchAndBound.h:903
BBSolution< Node, Value > SolutionT
the solution type
Definition BranchAndBound.h:887
bb_value_t< P > Value
the value type
Definition BranchAndBound.h:884
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
what evaluates and branches the nodes of a B&B
Definition BranchAndBound.h:315
a type of values of a B&B: signed, totally ordered, made from an int, and with its BBValueTraits
Definition BranchAndBound.h:262
a B&B problem
Definition BranchAndBound.h:337
a B&B problem that estimates the best feasible value under a node
Definition BranchAndBound.h:362
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
BBLeafKind
why a node is a leaf of the tree explored
Definition BranchAndBound.h:166
@ feasible
evaluated with a feasible value, and not branched
Definition BranchAndBound.h:169
@ open
left open by a limit
Definition BranchAndBound.h:167
@ pruned
its bound cannot improve the incumbent
Definition BranchAndBound.h:168
@ infeasible
found infeasible
Definition BranchAndBound.h:170
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
@ best_first
the least bound first
Definition BranchAndBound.h:138
@ best_estimate
the least estimate( n ) of the problem first
Definition BranchAndBound.h:141
@ breadth_first
the shallowest node first, the oldest among those
Definition BranchAndBound.h:140
@ depth_first
the deepest node first
Definition BranchAndBound.h:139
BBSolution< typename P::node_type, bb_value_t< P > > branch_and_bound(const P &problem, const BasicBBOptions< bb_value_t< P > > &o={})
the sequential B&B of a problem, with the given options
Definition BranchAndBound.h:1056
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
@ node_limit
the limit on the stored nodes was reached first
Definition Solution.h:49
BBStatus
how a B&B ended
Definition BranchAndBound.h:146
@ optimal
the incumbent is optimal within the tolerance
Definition BranchAndBound.h:147
@ time_limit
the time limit was reached first
Definition BranchAndBound.h:150
@ infeasible
no feasible solution exists (nor was given)
Definition BranchAndBound.h:148
BasicBBOptions<> BBOptions
the options of a B&B with values of type double
Definition BranchAndBound.h:407
BasicEvaluation<> Evaluation
the outcome of the evaluation of a node, with values of type double
Definition BranchAndBound.h:163
Sense
the sense of the optimization
Definition BranchAndBound.h:131
@ minimize
the least value is sought
Definition BranchAndBound.h:132
@ maximize
the largest value is sought
Definition BranchAndBound.h:133
a leaf of the tree explored, or a node to start a search from
Definition BranchAndBound.h:181
Value bound
Definition BranchAndBound.h:183
BBLeafKind kind
Definition BranchAndBound.h:185
unsigned depth
Definition BranchAndBound.h:184
Node node
Definition BranchAndBound.h:182
an open node
Definition BranchAndBound.h:508
V bound
its bound, in the minimization
Definition BranchAndBound.h:510
unsigned depth
its depth in the tree
Definition BranchAndBound.h:511
Node node
the node
Definition BranchAndBound.h:509
the outcome of a B&B
Definition BranchAndBound.h:472
Value bound
Definition BranchAndBound.h:477
BBStats stats
Definition BranchAndBound.h:479
std::vector< BBLeaf< Node, Value > > frontier
Definition BranchAndBound.h:480
BBStatus status
Definition BranchAndBound.h:474
bool feasible(void) const noexcept
true if a feasible value is known
Definition BranchAndBound.h:483
Value value
Definition BranchAndBound.h:476
std::optional< Node > node
Definition BranchAndBound.h:475
Value gap
Definition BranchAndBound.h:478
the counters of a B&B
Definition BranchAndBound.h:417
std::size_t max_open
the most open nodes of a queue
Definition BranchAndBound.h:429
std::uint64_t messages
batches of nodes sent
Definition BranchAndBound.h:428
std::uint64_t pruned_evaluated
nodes pruned by their bound
Definition BranchAndBound.h:425
std::uint64_t evaluated
nodes evaluated
Definition BranchAndBound.h:419
std::uint64_t sent
nodes sent to another worker
Definition BranchAndBound.h:427
std::uint64_t generated
children generated
Definition BranchAndBound.h:421
std::uint64_t branched
nodes branched
Definition BranchAndBound.h:420
double seconds
wall-clock time of the search
Definition BranchAndBound.h:430
std::vector< std::uint64_t > evaluated_by
evaluations per worker
Definition BranchAndBound.h:431
std::uint64_t pruned_extracted
nodes pruned when extracted
Definition BranchAndBound.h:424
void add(const BBStats &o)
adds the counters of a worker
Definition BranchAndBound.h:436
std::uint64_t infeasible
nodes found infeasible
Definition BranchAndBound.h:422
std::uint64_t incumbents
improvements of the incumbent
Definition BranchAndBound.h:426
std::uint64_t pruned_generated
children pruned when generated
Definition BranchAndBound.h:423
what the B&B needs to know of the type V of its values
Definition BranchAndBound.h:205
static bool within(V b, V l, V z, double g) noexcept
true if b is at most l + g ( z - l ), z being finite
Definition BranchAndBound.h:244
static constexpr bool finite(V v) noexcept
true if v is finite
Definition BranchAndBound.h:228
static V tolerance(V z, V a, double r) noexcept
the tolerance on the gap of the finite incumbent z, by the absolute tolerance a and the relative one ...
Definition BranchAndBound.h:234
static constexpr V none(void) noexcept
the value of a Solution with no feasible value
Definition BranchAndBound.h:220
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< Value > incumbent
Definition BranchAndBound.h:389
std::size_t node_limit
Definition BranchAndBound.h:397
std::function< void(Value) > on_incumbent
Definition BranchAndBound.h:402
double time_limit
Definition BranchAndBound.h:398
Selection selection
Definition BranchAndBound.h:385
Value abs_gap
Definition BranchAndBound.h:386
bool keep_frontier
Definition BranchAndBound.h:399
double rel_gap
Definition BranchAndBound.h:387
std::function< Value(void) > shared_incumbent
Definition BranchAndBound.h:396
double plunge_gap
Definition BranchAndBound.h:388
the outcome of the evaluation of a node, with values of type V
Definition BranchAndBound.h:155
V bound
a bound on the values of the node
Definition BranchAndBound.h:156
std::optional< V > feasible
a feasible value, n being its solution, if there is one
Definition BranchAndBound.h:158
bool infeasible
true if the node has no solution
Definition BranchAndBound.h:157
a node of the search graph
Definition Node.h:74