PBeFS
Parallel Best-First Search
Loading...
Searching...
No Matches
StealingBranchAndBound.h
Go to the documentation of this file.
1/*--------------------------------------------------------------------------*/
2/*--------------------- File StealingBranchAndBound.h ----------------------*/
3/*--------------------------------------------------------------------------*/
58/*--------------------------------------------------------------------------*/
59/*------------------------------ DEFINITIONS -------------------------------*/
60/*--------------------------------------------------------------------------*/
61
62#ifndef __PBeFS_StealingBranchAndBound
63 #define __PBeFS_StealingBranchAndBound
64 /* self-identification: #endif at the end of the file */
65
66/*--------------------------------------------------------------------------*/
67/*-------------------------------- INCLUDES --------------------------------*/
68/*--------------------------------------------------------------------------*/
69
70#include <atomic>
71
72#include <cstdint>
73
74#include <memory>
75
76#include <optional>
77
78#include <utility>
79
80#include <vector>
81
83
84#include "ADS/concurrent/StealablePriorityQueue.h"
85
86/*--------------------------------------------------------------------------*/
87/*------------------------------- NAMESPACE --------------------------------*/
88/*--------------------------------------------------------------------------*/
89
91namespace PBeFS_di_unipi_it
92{
93
94/*--------------------------------------------------------------------------*/
95/*---------------------- CLASS StealingBranchAndBound ----------------------*/
96/*--------------------------------------------------------------------------*/
98
115
116template< BranchAndBound P , ADS::WorkerExecutor Executor = ADS::ThreadPool >
118
119/*------------------------ PUBLIC PART OF THE CLASS ------------------------*/
120
121 public:
122
123/*------------------------------ PUBLIC TYPES ------------------------------*/
124
125 using Node = typename P::node_type;
130
131/*------------------------------ CONSTRUCTOR -------------------------------*/
132
134
135 explicit StealingBranchAndBound( const P & problem ,
136 const Options & options = {} )
137 : f_p( problem ) , f_options( options ) {
138 f_options.normalize();
139 }
140
141/*------------------------- OTHER INITIALIZATIONS --------------------------*/
145
146 void set_executor( Executor & ex ) noexcept { f_ex = &ex; }
147
148/*-------------------------------- SOLVING ---------------------------------*/
150
151 [[nodiscard]] SolutionT solve( void ) const {
152 return( run( std::nullopt ) );
153 }
154
155/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
159
160 [[nodiscard]] SolutionT solve( std::vector< Leaf > seeds ) const {
161 return( run( std::move( seeds ) ) );
162 }
163
164/*----------------------- PRIVATE PART OF THE CLASS ------------------------*/
165
166 private:
167
168 // a node in a queue: the node and its depth, its bound being the bound of
169 // the pair
170 struct Held {
171 Node node; // the node
172 unsigned depth; // its depth in the tree
173 };
174
175 // the order of the selection: the key, then the push, reversed (the last
176 // pushed first) unless first in, first out
177 using Order = std::pair< Value , std::uint64_t >;
178
179 using Frontier = ADS::StealingFrontier< Order , Held , std::less<> , Value >;
180 using Item = typename BBQueue< Node , Value >::Item;
181
182/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
183
184 SolutionT run( std::optional< std::vector< Leaf > > seeds ) const
185 {
187 const unsigned p = f_options.threads;
188 Stopwatch clock;
189 const Value start = f_options.incumbent ?
190 detail::bb_signed< P >( *f_options.incumbent ) : inf;
191 Shared sh( p , start , f_options.seed );
192
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 );
196 std::size_t n = 0;
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 } );
199
200 const auto body = [ & ]( std::size_t i ) {
201 if( ! ADS::isRunCaller() )
202 pin_worker( unsigned( i ) );
203 w[ i ]->run();
204 };
205 if( f_ex )
206 sh.frontier.run( *f_ex , body );
207 else {
208 Executor ex( p );
209 sh.frontier.run( ex , body );
210 }
211
212 // the nodes left open by a limit bound the optimum too
213 SolutionT sol;
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 ) );
218 if( x->f_dive )
219 fr.add( std::move( x->f_dive->node ) , x->f_dive->bound ,
220 x->f_dive->depth , BBLeafKind::open );
221 }
222 sh.frontier.leftovers( [ & ]( auto && e ) {
223 fr.add( std::move( e.item.node ) , e.bound , e.item.depth ,
225 } );
226 sol.stats.sent = sh.frontier.steals();
227 detail::bb_finish< P >( sol , sh.inc , fr , sh.limit.load() ,
228 sh.time.load() );
229 sol.stats.seconds = clock.seconds();
230 return( sol );
231 }
232
233/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
234 // the data shared by the workers
235
236 struct Shared {
237
238 Shared( unsigned np , Value z , std::uint64_t seed )
239 : frontier( np , seed ) , inc( z ) {}
240
241 Frontier frontier; // the queues
242 BBIncumbent< Node , Value > inc; // the incumbent
243 alignas( cache_line ) std::atomic< std::uint64_t > evaluated{ 0 };
244 std::atomic< bool > limit{ false }; // stopped by a limit
245 std::atomic< bool > time{ false }; // ... the time limit
246 };
247
248/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
249 // a worker: its counters, its dive and its leaves; its queue is in the
250 // frontier
251
252 struct alignas( cache_line ) Worker {
253
254 Worker( const StealingBranchAndBound & a , Shared & s , unsigned i ,
255 const Stopwatch & c )
256 : f_a( a ) , f_sh( s ) , f_id( i ) , f_clock( c ) ,
257 f_fifo( a.f_options.selection == Selection::breadth_first ) ,
258 f_fr( a.f_options.keep_frontier ) {}
259
260 // the main loop
261 void run( void ) {
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 ) );
267 };
268
269 for( ;; ) {
270 std::optional< Item > it = next();
271 if( ! it )
272 return;
273 if( stop() ) { // the node stays open
274 f_fr.add( std::move( it->node ) , it->bound , it->depth ,
276 return;
277 }
278
279 v_kids.clear();
280 detail::bb_process< P >( it->node , it->bound , it->depth , w , inc ,
281 improve , [ & ]( Node && c , Value b ,
282 unsigned d ) {
283 v_kids.push_back( Item{ std::move( c ) , b ,
284 d } );
285 } , o , f_st , f_fr );
286 f_sh.evaluated.fetch_add( 1 );
287
288 const std::size_t k = detail::bb_dive( v_kids , best_bound() , inc() ,
289 o );
290 for( std::size_t j = 0 ; j < v_kids.size() ; ++j )
291 if( j == k )
292 f_dive.emplace( std::move( v_kids[ j ] ) );
293 else
294 push( std::move( v_kids[ j ] ) );
295 }
296 }
297
298 // pushes a node in the queue of the worker
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() );
307 }
308
309 // the least bound of the nodes of the queue of the worker, +infinity if
310 // there is none
311 [[nodiscard]] Value best_bound( void ) const {
312 const auto b = f_sh.frontier.minBound( f_id );
313 return( b ? *b : BBValueTraits< Value >::infinity() );
314 }
315
316 // the next node: the dive, the best of the queue, or the best of what is
317 // stolen; none when the search is over, i.e., when no worker holds a
318 // node or a limit is reached; the queue is dropped when none of its
319 // nodes can improve the incumbent
320 std::optional< Item > next( void ) {
321 if( f_dive ) {
322 std::optional< Item > it( std::move( f_dive ) );
323 f_dive.reset();
324 return( it );
325 }
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 ,
331 } );
332 auto e = f_sh.frontier.pop( f_id );
333 if( ! e )
334 return( std::nullopt );
335 return( Item{ std::move( e->item.node ) , e->bound , e->item.depth } );
336 }
337
338 // true if a limit is reached, which ends the search
339 bool stop( void ) {
340 if( ! detail::bb_limit( f_a.f_options , f_sh.evaluated.load() , f_clock ,
341 f_sh.limit , f_sh.time ) )
342 return( false );
343 f_sh.frontier.stop();
344 return( true );
345 }
346
347 const StealingBranchAndBound & f_a; // the search
348 Shared & f_sh; // the shared data
349 unsigned f_id; // the index of the worker
350 const Stopwatch & f_clock; // the clock of the search
351 bool f_fifo; // first in, first out
352 std::uint64_t f_seq = 0; // the pushes so far
353 std::optional< Item > f_dive; // the node of its dive
354 std::vector< Item > v_kids; // the children of a node
355 BBStats f_st; // its counters
356 detail::BBFrontier< Node , Value > f_fr; // its leaves
357 };
358
359/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
360
361 const P & f_p;
362 Options f_options;
363 Executor * f_ex = nullptr;
364
365 }; // end( class( StealingBranchAndBound ) )
366
367/*--------------------------------------------------------------------------*/
368
369} // end( namespace PBeFS_di_unipi_it )
370
371/*--------------------------------------------------------------------------*/
372
373#endif /* __PBeFS_StealingBranchAndBound */
374
375/*--------------------------------------------------------------------------*/
376/*------------------- End File StealingBranchAndBound.h --------------------*/
377/*--------------------------------------------------------------------------*/
Header file for the parallel best-first searches with an incumbent, i.e., the parallel Branch-and-Bou...
BasicParallelBBOptions< Value > Options
the options
Definition StealingBranchAndBound.h:127
typename P::node_type Node
the node type
Definition StealingBranchAndBound.h:125
BBLeaf< Node, Value > Leaf
the leaf type
Definition StealingBranchAndBound.h:128
SolutionT solve(std::vector< Leaf > seeds) const
searches the subtrees of the given nodes, returning the Solution [see BranchAndBoundSearch::solve()];...
Definition StealingBranchAndBound.h:160
SolutionT solve(void) const
searches the tree from the root, returning the Solution
Definition StealingBranchAndBound.h:151
BBSolution< Node, Value > SolutionT
the solution type
Definition StealingBranchAndBound.h:129
StealingBranchAndBound(const P &problem, const Options &options={})
constructor, taking the problem (which must outlive the search)
Definition StealingBranchAndBound.h:135
void set_executor(Executor &ex) noexcept
sets the Executor that runs the workers, which must outlive the searches; without it,...
Definition StealingBranchAndBound.h:146
bb_value_t< P > Value
the value type
Definition StealingBranchAndBound.h:126
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
@ breadth_first
the shallowest node first, the oldest among those
Definition BranchAndBound.h:140
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
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
void pin_worker(unsigned i) noexcept
pins the calling thread to the i-th CPU it may run on, if PBEFS_PIN is set
Definition SearchUtils.h:77
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
std::optional< V > incumbent
a known feasible value, if any
Definition BranchAndBound.h:389
a node of the search graph
Definition Node.h:74
std::uint64_t seed
seed of the owners
Definition SearchUtils.h:99
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