PBeFS
Parallel Best-First Search
Loading...
Searching...
No Matches
FarmBranchAndBound.h
Go to the documentation of this file.
1/*--------------------------------------------------------------------------*/
2/*----------------------- File FarmBranchAndBound.h ------------------------*/
3/*--------------------------------------------------------------------------*/
51/*--------------------------------------------------------------------------*/
52/*------------------------------ DEFINITIONS -------------------------------*/
53/*--------------------------------------------------------------------------*/
54
55#ifndef __PBeFS_FarmBranchAndBound
56 #define __PBeFS_FarmBranchAndBound
57 /* self-identification: #endif at the end of the file */
58
59/*--------------------------------------------------------------------------*/
60/*-------------------------------- INCLUDES --------------------------------*/
61/*--------------------------------------------------------------------------*/
62
63#include <atomic>
64
65#include <memory>
66
67#include <stdexcept>
68
69#include <vector>
70
72
73#include "ADS/parallel/Farm.h"
74
75/*--------------------------------------------------------------------------*/
76/*------------------------------- NAMESPACE --------------------------------*/
77/*--------------------------------------------------------------------------*/
78
80namespace PBeFS_di_unipi_it
81{
82
83/*--------------------------------------------------------------------------*/
84/*------------------------ CLASS FarmBranchAndBound ------------------------*/
85/*--------------------------------------------------------------------------*/
87
102
103template< BranchAndBound P , ADS::WorkerExecutor Executor = ADS::ThreadPool >
105
106/*------------------------ PUBLIC PART OF THE CLASS ------------------------*/
107
108 public:
109
110/*------------------------------ PUBLIC TYPES ------------------------------*/
111
112 using Node = typename P::node_type;
117
118/*------------------------------ CONSTRUCTOR -------------------------------*/
119
122
123 explicit FarmBranchAndBound( const P & problem ,
124 const Options & options = {} )
125 : f_p( problem ) , f_options( options ) {
126 f_options.normalize();
127 }
128
129/*------------------------- OTHER INITIALIZATIONS --------------------------*/
133
134 void set_executor( Executor & ex ) noexcept { f_ex = &ex; }
135
136/*-------------------------------- SOLVING ---------------------------------*/
138
139 [[nodiscard]] SolutionT solve( void ) const {
140 return( run( std::nullopt ) );
141 }
142
143/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
146
147 [[nodiscard]] SolutionT solve( std::vector< Leaf > seeds ) const {
148 return( run( std::move( seeds ) ) );
149 }
150
151/*----------------------- PRIVATE PART OF THE CLASS ------------------------*/
152
153 private:
154
155 using Item = typename BBQueue< Node , Value >::Item;
156
157 SolutionT run( std::optional< std::vector< Leaf > > seeds ) const
158 {
160 Stopwatch clock;
161 const Value start = f_options.incumbent ?
162 detail::bb_signed< P >( *f_options.incumbent ) : inf;
163 Shared sh( start , clock );
164 Master master( *this , sh , detail::bb_starts( f_p , std::move( seeds ) ) );
165 const unsigned n = f_options.threads;
166 std::vector< std::unique_ptr< Worker > > w( n );
167 for( unsigned i = 0 ; i < n ; ++i )
168 w[ i ] = std::make_unique< Worker >( *this , sh , i );
169 const auto begin = [ & ]( auto && emit , std::size_t ) {
170 if( ! ADS::isRunCaller() )
171 pin_worker( 0 );
172 master.start( emit );
173 };
174 const auto back = [ & ]( Task * t , auto && emit ) {
175 return( master.back( t , emit ) );
176 };
177 const auto work = [ & ]( Task * t , std::size_t i ) {
178 return( w[ i ]->process( t ) );
179 };
180 if( f_ex )
181 ADS::feedbackFarm< Task * , Task * >( *f_ex , n , begin , back , work );
182 else {
183 Executor ex( n + 1 );
184 ADS::feedbackFarm< Task * , Task * >( ex , n , begin , back , work );
185 }
186
187 SolutionT sol;
188 // left open by a limit
189 detail::BBFrontier< Node , Value > & fr = master.f_fr;
190 fr.add( master.f_q , BBLeafKind::open );
191 for( auto & x : w ) {
192 sol.stats.add( x->f_st );
193 fr.merge( std::move( x->f_fr ) );
194 }
195 sol.stats.pruned_extracted += master.f_st.pruned_extracted;
196 sol.stats.max_open = master.f_st.max_open;
197 sol.stats.messages = master.f_st.messages;
198 detail::bb_finish< P >( sol , sh.inc , fr , sh.limit.load() ,
199 sh.time.load() );
200 sol.stats.seconds = clock.seconds();
201 return( sol );
202 }
203
204 // a task: the nodes to evaluate, and then the children that survive
205 struct Task {
206 std::vector< Item > nodes; // the nodes
207 std::vector< Item > kids; // their children
208 Value best = Value( 0 ); // the best open bound
209 };
210
211/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
212 // the data shared by the master and the workers
213
214 struct Shared {
215
216 Shared( Value z , const Stopwatch & c ) : inc( z ) , clock( c ) {}
217
218 BBIncumbent< Node , Value > inc; // the incumbent
219 const Stopwatch & clock; // the clock of the search
220 alignas( cache_line ) std::atomic< std::uint64_t > evaluated{ 0 };
221 std::atomic< bool > limit{ false }; // stopped by a limit
222 std::atomic< bool > time{ false }; // ... the time limit
223 };
224
225/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
226 // the master: the queue of the open nodes
227
228 struct Master {
229
230 Master( const FarmBranchAndBound & a , Shared & s ,
231 std::vector< Leaf > && starts )
232 : f_a( a ) , f_sh( s ) , f_q( a.f_options.selection ) ,
233 f_fr( a.f_options.keep_frontier ) , v_starts( std::move( starts ) ) {}
234
235 Master( const Master & ) = delete;
236 Master & operator=( const Master & ) = delete;
237
238 ~Master() {
239 for( Task * t : v_free )
240 delete t;
241 }
242
243 // the start: the first nodes, and the first tasks
244 template< class E >
245 void start( E && emit ) {
246 for( auto & l : v_starts )
247 enqueue( Item{ std::move( l.node ) , l.bound , l.depth } );
248 v_starts.clear();
249 check_limits();
250 dispatch( emit );
251 }
252
253 // a task back, with the children of its nodes; false once nothing is
254 // out at the workers and nothing is left to send
255 template< class E >
256 bool back( Task * t , E && emit ) {
257 --f_out;
258 for( Item & it : t->kids )
259 enqueue( std::move( it ) );
260 t->kids.clear();
261 v_free.push_back( t );
262 check_limits();
263 dispatch( emit );
264 return( ( f_out > 0 ) || ( ! f_sh.limit.load() && ! f_q.empty() ) );
265 }
266
267 // pushes a node in the queue
268 void enqueue( Item && it ) {
269 const Value key = detail::bb_key( f_a.f_p , it.node , it.bound ,
270 it.depth , f_a.f_options.selection );
271 f_q.push( std::move( it.node ) , it.bound , it.depth , key );
272 f_st.max_open = std::max( f_st.max_open , f_q.size() );
273 }
274
275 // stops the dispatch if a limit is reached
276 void check_limits( void ) {
277 ( void ) detail::bb_limit( f_a.f_options , f_sh.evaluated.load() ,
278 f_sh.clock , f_sh.limit , f_sh.time );
279 }
280
281 // sends tasks while there are nodes and free workers
282 template< class E >
283 void dispatch( E && emit ) {
284 while( ! f_sh.limit.load() && ( f_out < f_a.f_options.threads ) &&
285 ! f_q.empty() ) {
286 const Value best = f_q.best_bound();
287 if( detail::bb_prunable( best , f_sh.inc.value() , f_a.f_options ) ) {
288 f_st.pruned_extracted += f_fr.add( f_q , BBLeafKind::pruned );
289 return; // nothing can improve
290 }
291 Task * t;
292 if( v_free.empty() )
293 t = new Task;
294 else {
295 t = v_free.back();
296 v_free.pop_back();
297 }
298 t->nodes.clear();
299 while( ! f_q.empty() && ( t->nodes.size() < f_a.f_options.batch ) )
300 t->nodes.push_back( f_q.pop() );
301 t->best = std::min( best , f_q.best_bound() );
302 ++f_out;
303 ++f_st.messages;
304 emit( t );
305 }
306 }
307
308 const FarmBranchAndBound & f_a; // the search
309 Shared & f_sh; // the shared data
310 BBQueue< Node , Value > f_q; // the open nodes
311 std::vector< Task * > v_free; // tasks to reuse
312 unsigned f_out = 0; // tasks at the workers
313 BBStats f_st; // its counters
314 detail::BBFrontier< Node , Value > f_fr; // its leaves
315 std::vector< Leaf > v_starts; // the nodes to start from
316 };
317
318/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
319 // a worker: evaluates and branches the nodes of a task
320
321 struct Worker {
322
323 Worker( const FarmBranchAndBound & a , Shared & s , unsigned i )
324 : f_a( a ) , f_sh( s ) , f_id( i ) ,
325 f_fr( a.f_options.keep_frontier ) {}
326
327 // the nodes of t evaluated and branched, the children in t
328 Task * process( Task * t ) {
329 if( ! f_w ) { // the first task, on its own thread
330 if( ! ADS::isRunCaller() )
331 pin_worker( f_id + 1 );
332 f_w.emplace( detail::bb_worker( f_a.f_p , f_id ) );
333 }
334 const auto & o = f_a.f_options;
335 const auto inc = [ & ] { return( f_sh.inc.value() ); };
336 const auto improve = [ & ]( Value v , const Node & n ) {
337 return( f_sh.inc.template improve< P >( v , n , o ) );
338 };
339 for( Item & first : t->nodes ) {
340 std::optional< Item > it( std::move( first ) );
341 while( it ) { // the node, then the child that dives
342 v_kids.clear();
343 detail::bb_process< P >( it->node , it->bound , it->depth , *f_w , inc ,
344 improve , [ & ]( Node && c , Value b ,
345 unsigned d ) {
346 v_kids.push_back( Item{ std::move( c ) , b ,
347 d } );
348 } , o , f_st , f_fr );
349 f_sh.evaluated.fetch_add( 1 );
350 it.reset();
351 const std::size_t k = detail::bb_dive( v_kids , t->best , inc() , o );
352 for( std::size_t j = 0 ; j < v_kids.size() ; ++j )
353 if( j == k )
354 it.emplace( std::move( v_kids[ j ] ) );
355 else
356 t->kids.push_back( std::move( v_kids[ j ] ) );
357 if( it && o.node_limit &&
358 ( f_sh.evaluated.load() >= o.node_limit ) ) {
359 t->kids.push_back( std::move( *it ) ); // the dive stops here
360 it.reset();
361 }
362 }
363 }
364 t->nodes.clear();
365 return( t );
366 }
367
368 using Evaluator = decltype( detail::bb_worker( std::declval< const P & >() ,
369 0u ) );
370
371 const FarmBranchAndBound & f_a; // the search
372 Shared & f_sh; // the shared data
373 unsigned f_id; // the index of the worker
374 std::optional< Evaluator > f_w; // its evaluator
375 std::vector< Item > v_kids; // the children of a node
376 BBStats f_st; // its counters
377 detail::BBFrontier< Node , Value > f_fr; // its leaves
378 };
379
380/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
381
382 const P & f_p;
383 Options f_options;
384 Executor * f_ex = nullptr;
385
386 }; // end( class( FarmBranchAndBound ) )
387
388/*--------------------------------------------------------------------------*/
389
390} // end( namespace PBeFS_di_unipi_it )
391
392/*--------------------------------------------------------------------------*/
393
394#endif /* __PBeFS_FarmBranchAndBound */
395
396/*--------------------------------------------------------------------------*/
397/*--------------------- End File FarmBranchAndBound.h ----------------------*/
398/*--------------------------------------------------------------------------*/
Header file for the parallel best-first searches with an incumbent, i.e., the parallel Branch-and-Bou...
BasicParallelBBOptions< Value > Options
the options
Definition FarmBranchAndBound.h:114
typename P::node_type Node
the node type
Definition FarmBranchAndBound.h:112
BBLeaf< Node, Value > Leaf
the leaf type
Definition FarmBranchAndBound.h:115
SolutionT solve(std::vector< Leaf > seeds) const
searches the subtrees of the given nodes, returning the Solution [see BranchAndBoundSearch::solve()]
Definition FarmBranchAndBound.h:147
FarmBranchAndBound(const P &problem, const Options &options={})
constructor, taking the problem (which must outlive the search); threads is the number of workers,...
Definition FarmBranchAndBound.h:123
SolutionT solve(void) const
searches the tree from the root, returning the Solution
Definition FarmBranchAndBound.h:139
BBSolution< Node, Value > SolutionT
the solution type
Definition FarmBranchAndBound.h:116
void set_executor(Executor &ex) noexcept
sets the Executor that runs the master and the workers, which must outlive the searches and have at l...
Definition FarmBranchAndBound.h:134
bb_value_t< P > Value
the value type
Definition FarmBranchAndBound.h:113
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
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
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
an open node
Definition BranchAndBound.h:508
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
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