84#ifndef __PBeFS_BranchAndBound
85 #define __PBeFS_BranchAndBound
108#include <type_traits>
116#include "ADS/priority/IndexedHeapPair.h"
154template<
class V =
double >
180template<
class Node ,
class V =
double >
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" );
212 [[nodiscard]]
static constexpr V
infinity(
void )
noexcept {
213 if constexpr( std::numeric_limits< V >::has_infinity )
214 return( std::numeric_limits< V >::infinity() );
216 return( std::numeric_limits< V >::max() );
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() );
228 [[nodiscard]]
static constexpr bool finite( V v )
noexcept {
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 ) ) ) );
238 using L =
long double;
239 return( std::max( a , V( std::floor( L( r ) * std::abs( L( z ) ) ) ) ) );
244 [[nodiscard]]
static bool within( V b , V l , V z ,
double g )
noexcept {
247 if constexpr( std::is_floating_point_v< V > )
248 return( b <= l + g * ( z - l ) );
250 using L =
long double;
251 return( L( b ) <= L( l ) + L( g ) * ( L( z ) - L( l ) ) );
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 >;
272 -> std::convertible_to< bool >;
278 template<
class N ,
class V >
280 void operator()(
const N & , V )
const noexcept {}
285 struct bb_value {
using type = double; };
287 template<
class P >
requires requires {
typename P::value_type; }
288 struct bb_value< P > {
using type =
typename P::value_type; };
314template<
class W ,
class N ,
class V =
double >
316 { w.evaluate( n ) } -> std::convertible_to< BasicEvaluation< V > >;
317 w.branch( cn , detail::bb_sink< N , V >{} );
338 typename P::node_type;
339 requires std::copyable< typename P::node_type >;
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 > >;
354 requires(
const P & p ,
const typename P::node_type & n ) {
355 { p.hash( n ) } -> std::convertible_to< std::uint64_t >;
363 requires(
const P & p ,
const typename P::node_type & n ) {
364 { p.estimate( n ) } -> std::convertible_to< bb_value_t< P > >;
382template<
class V =
double >
471template<
class Node ,
class V =
double >
483 [[nodiscard]]
bool feasible(
void )
const noexcept {
500template<
class Node ,
class V =
double >
523 void push(
Node && n , V bound ,
unsigned depth , V key ) {
525 if( v_free.empty() ) {
527 v_slot.emplace_back();
528 if( i >= std::size_t( f_q.ids() ) )
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 );
543 [[nodiscard]]
const Item &
top(
void )
const {
544 return( *v_slot[ std::size_t( f_q.top() ) ] );
551 const auto i = std::size_t( f_q.pop() );
552 Item it = std::move( *v_slot[ i ] );
554 v_free.push_back( i );
569 const std::size_t n = f_q.size();
582 for(
auto & x : v_slot )
584 f( std::move( *x ) );
591 [[nodiscard]]
bool empty(
void )
const noexcept {
return( f_q.empty() ); }
596 [[nodiscard]] std::size_t
size(
void )
const noexcept {
597 return( f_q.size() );
606 using Order = std::pair< V , std::uint64_t >;
609 using Queue = ADS::IndexedHeapPair< Order , V >;
612 void grow( std::size_t 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 );
623 std::uint64_t f_seq = 0;
624 std::vector< std::optional< Item > > v_slot;
625 std::vector< std::size_t > v_free;
641 BasicEvaluation< bb_value_t< P > > evaluate(
typename P::node_type & n ) {
642 return( p.evaluate( n ) );
645 void branch(
const typename P::node_type & n , F && f ) {
646 p.branch( n , std::forward< F >( f ) );
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 ) );
659 return( BBSelf< P >{ p } );
667template< BranchAndBound P ,
class V >
668constexpr V bb_signed(
const V & v ) {
679bool bb_prunable(
const V & bound ,
const V & inc ,
680 const BasicBBOptions< V > & o )
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 ) );
695template<
class Node ,
class V =
double >
698 explicit BBFrontier(
bool k =
false ) : keep( k ) {}
702 void add( Node && n , V b ,
unsigned depth ,
BBLeafKind kind ,
703 bool lower =
true ) {
705 low = std::min( low , b );
707 leaves.push_back( BBLeaf< Node , V >{ std::move( n ) , b , depth ,
713 std::size_t add( BBQueue< Node , V > & q ,
BBLeafKind kind ) {
714 low = std::min( low , q.best_bound() );
717 return( q.drain( [ & ](
auto && it ) {
718 leaves.push_back( BBLeaf< Node , V >{ std::move( it.node ) , it.bound ,
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() ) );
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 );
742 std::vector< BBLeaf< Node , V > > leaves;
750template< BranchAndBound P ,
class Seeds >
751std::vector< BBLeaf< typename P::node_type , bb_value_t< P > > > bb_starts(
755 using Node =
typename P::node_type;
757 std::vector< BBLeaf< Node , V > > v;
763 v = std::move( *seeds );
765 l.bound = bb_signed< P >( l.bound );
773template< BranchAndBound P >
806 W & w , Inc && inc , Improve && improve , Push && push ,
810 using Node =
typename P::node_type;
813 if( bb_prunable( bound , inc() , o ) ) {
814 ++st.pruned_extracted;
818 const auto cutoff = [ & ]( void ) {
819 if constexpr(
requires { w.cutoff( inc() ); } )
820 w.cutoff( bb_signed< P >( inc() ) );
830 const V b = std::max( bound , bb_signed< P >( e.bound ) );
831 if( e.feasible && improve( bb_signed< P >( *e.feasible ) , n ) )
835 if( bb_prunable( b , inc() , o ) ) {
836 ++st.pruned_evaluated;
837 fr.add( std::move( n ) , b , depth , kind );
841 const std::uint64_t g = st.generated;
843 w.branch( std::as_const( n ) , [ & ](
auto && c , V est ) {
845 const V cb = std::max( b , bb_signed< P >( est ) );
846 if( bb_prunable( cb , inc() , o ) ) {
847 ++st.pruned_generated;
849 fr.add(
Node( std::forward<
decltype( c ) >( c ) ) , cb , depth + 1 ,
852 fr.low = std::min( fr.low , cb );
855 push(
Node( std::forward<
decltype( c ) >( c ) ) , cb , depth + 1 );
857 if( st.generated == g )
858 fr.add( std::move( n ) , b , depth , kind ,
false );
874template< BranchAndBound P >
883 using Node =
typename P::node_type;
897 unsigned worker = 0 )
898 : f_p( problem ) , f_options( options ) , f_worker( worker ) {}
904 return( run( std::nullopt ) );
918 return( run( std::move( seeds ) ) );
925 SolutionT run( std::optional< std::vector< Leaf > > seeds )
const
928 const Value inf = Tr::infinity();
929 const Options & o = f_options;
934 auto w = detail::bb_worker( f_p , f_worker );
936 Value inc = o.incumbent ? detail::bb_signed< P >( *o.incumbent ) : inf;
938 std::optional< Node > inc_node;
939 const auto improve = [ & ]( Value v ,
const Node & n ) {
945 o.on_incumbent( detail::bb_signed< P >( v ) );
949 using Item =
typename BBQueue< Node , Value >::Item;
950 BBQueue< Node , Value > q( sel );
951 std::optional< Item > dive;
952 std::vector< Item > kids;
953 detail::BBFrontier< Node , Value > fr( o.keep_frontier );
955 const auto push = [ & ]( Item && it ) {
956 const Value key = detail::bb_key( f_p , it.node , it.bound , it.depth ,
958 q.push( std::move( it.node ) , it.bound , it.depth , key );
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;
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;
973 if( ( o.time_limit > 0 ) && ( clock.
seconds() >= o.time_limit ) ) {
974 sol.status = BBStatus::time_limit;
977 const Value best = std::min( q.best_bound() ,
978 dive ? dive->bound : inf );
979 if( q.empty() && ! dive )
981 if( detail::bb_prunable( best , cut , o ) ) {
985 fr.add( std::move( dive->node ) , dive->bound , dive->depth ,
986 BBLeafKind::pruned );
992 Item it = dive ? std::move( *dive ) : q.pop();
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 } );
1004 std::size_t k = kids.size();
1005 if( sel == Selection::plunging ) {
1007 for( std::size_t j = 1 ; j < kids.size() ; ++j )
1008 if( kids[ j ].bound <= kids[ c ].bound )
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 ) )
1015 for( std::size_t j = 0 ; j < kids.size() ; ++j )
1017 dive.emplace( std::move( kids[ j ] ) );
1019 push( std::move( kids[ j ] ) );
1023 fr.add( q , BBLeafKind::open );
1025 fr.add( std::move( dive->node ) , dive->bound , dive->depth ,
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 };
1054template< BranchAndBound P >
1055[[nodiscard]] BBSolution< typename P::node_type , bb_value_t< P > >
1059 return( BranchAndBoundSearch< P >( problem , o ).solve() );
1066template< BranchAndBound P >
1067[[nodiscard]] BBSolution< typename P::node_type , bb_value_t< P > >
1069 std::vector<
BBLeaf<
typename P::node_type ,
1073 return( BranchAndBoundSearch< P >( problem , o ).solve(
1074 std::move( seeds ) ) );
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