PBeFS
Parallel Best-First Search
Loading...
Searching...
No Matches
DeterministicBranchAndBound.h
Go to the documentation of this file.
1/*--------------------------------------------------------------------------*/
2/*------------------- File DeterministicBranchAndBound.h -------------------*/
3/*--------------------------------------------------------------------------*/
56/*--------------------------------------------------------------------------*/
57/*------------------------------ DEFINITIONS -------------------------------*/
58/*--------------------------------------------------------------------------*/
59
60#ifndef __PBeFS_DeterministicBranchAndBound
61 #define __PBeFS_DeterministicBranchAndBound
62 /* self-identification: #endif at the end of the file */
63
64/*--------------------------------------------------------------------------*/
65/*-------------------------------- INCLUDES --------------------------------*/
66/*--------------------------------------------------------------------------*/
67
68#include <algorithm>
69
70#include <barrier>
71
72#include <exception>
73
74#include <mutex>
75
76#include <optional>
77
78#include <utility>
79
80#include <vector>
81
83
84/*--------------------------------------------------------------------------*/
85/*------------------------------- NAMESPACE --------------------------------*/
86/*--------------------------------------------------------------------------*/
87
89namespace PBeFS_di_unipi_it
90{
91
92/*--------------------------------------------------------------------------*/
93/*------------------- CLASS DeterministicBranchAndBound --------------------*/
94/*--------------------------------------------------------------------------*/
96
117
118template< BranchAndBound P , ADS::WorkerExecutor Executor = ADS::ThreadPool >
120
121/*------------------------ PUBLIC PART OF THE CLASS ------------------------*/
122
123 public:
124
125/*------------------------------ PUBLIC TYPES ------------------------------*/
126
127 using Node = typename P::node_type;
132
133/*------------------------------ CONSTRUCTOR -------------------------------*/
134
136
137 explicit DeterministicBranchAndBound( const P & problem ,
138 const Options & options = {} )
139 : f_p( problem ) , f_options( options ) {
140 f_options.normalize();
141 }
142
143/*------------------------- OTHER INITIALIZATIONS --------------------------*/
147
148 void set_executor( Executor & ex ) noexcept { f_ex = &ex; }
149
150/*-------------------------------- SOLVING ---------------------------------*/
152
153 [[nodiscard]] SolutionT solve( void ) const {
154 return( run( std::nullopt ) );
155 }
156
157/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
160
161 [[nodiscard]] SolutionT solve( std::vector< Leaf > seeds ) const {
162 return( run( std::move( seeds ) ) );
163 }
164
165/*----------------------- PRIVATE PART OF THE CLASS ------------------------*/
166
167 private:
168
169 using Item = typename BBQueue< Node , Value >::Item;
170
171/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
172 // a node of a round, and what its evaluation gives
173
174 struct Slot {
175 Item it; // the node
176 std::vector< Item > kids; // its children
177 std::optional< std::pair< Value , Node > > best; // its best value
178 BBStats st; // its counters
179 detail::BBFrontier< Node , Value > fr; // its leaves
180 };
181
182/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
183
184 SolutionT run( std::optional< std::vector< Leaf > > seeds ) const
185 {
187 const Options & o = f_options;
188 const unsigned p = o.threads;
189 const std::size_t round = o.round ? o.round : p;
190 Stopwatch clock;
191
192 BBQueue< Node , Value > q( o.selection );
193 SolutionT sol;
194 BBStats & st = sol.stats;
195 st.evaluated_by.assign( p , 0 );
196 const auto push = [ & ]( Item && it ) {
197 const Value key = detail::bb_key( f_p , it.node , it.bound , it.depth ,
198 o.selection );
199 q.push( std::move( it.node ) , it.bound , it.depth , key );
200 st.max_open = std::max( st.max_open , q.size() );
201 };
202 for( auto & l : detail::bb_starts( f_p , std::move( seeds ) ) )
203 push( Item{ std::move( l.node ) , l.bound , l.depth } );
204
205 BBIncumbent< Node , Value > inc( o.incumbent ?
206 detail::bb_signed< P >( *o.incumbent ) :
207 inf );
208 detail::BBFrontier< Node , Value > fr( o.keep_frontier );
209 std::vector< Slot > slots( round );
210 std::size_t nslots = 0; // the nodes of the round
211 Value snapshot = inc.value(); // the incumbent of the round
212 bool done = false , limit = false , late = false;
213 std::exception_ptr error;
214 std::mutex error_m;
215
216 // merges the outcomes of the round just ended, in the order of its
217 // nodes, and extracts the nodes of the next one; it is run by one worker
218 // while the others wait
219 const auto next_round = [ & ]( void ) noexcept {
220 try {
221 for( std::size_t j = 0 ; j < nslots ; ++j ) {
222 Slot & s = slots[ j ];
223 s.st.incumbents = 0; // counted here, by the merge
224 add_counters( st , s.st );
225 st.evaluated_by[ j % p ] += s.st.evaluated;
226 if( s.best && inc.template improve< P >( s.best->first ,
227 s.best->second , o ) )
228 ++st.incumbents;
229 fr.merge( std::move( s.fr ) );
230 for( Item & k : s.kids )
231 if( detail::bb_prunable( k.bound , inc.value() , o ) ) {
232 ++st.pruned_generated;
233 fr.add( std::move( k.node ) , k.bound , k.depth ,
235 }
236 else
237 push( std::move( k ) );
238 }
239 nslots = 0;
240
241 if( error ) {
242 done = true;
243 return;
244 }
245 if( o.node_limit && ( st.evaluated >= o.node_limit ) ) {
246 limit = done = true;
247 return;
248 }
249 if( ( o.time_limit > 0 ) && ( clock.seconds() >= o.time_limit ) ) {
250 limit = late = done = true;
251 return;
252 }
253 if( q.empty() ) {
254 done = true;
255 return;
256 }
257 if( detail::bb_prunable( q.best_bound() , inc.value() , o ) ) {
258 st.pruned_extracted += fr.add( q , BBLeafKind::pruned );
259 done = true; // nothing can improve
260 return;
261 }
262
263 std::size_t r = round;
264 if( o.node_limit )
265 r = std::min( r , std::size_t( o.node_limit - st.evaluated ) );
266 while( ( nslots < r ) && ! q.empty() ) {
267 Slot & s = slots[ nslots++ ];
268 s.it = q.pop();
269 s.kids.clear();
270 s.best.reset();
271 s.st = BBStats();
272 s.fr = detail::BBFrontier< Node , Value >( o.keep_frontier );
273 }
274 snapshot = inc.value();
275 }
276 catch( ... ) {
277 std::lock_guard< std::mutex > guard( error_m );
278 if( ! error )
279 error = std::current_exception();
280 done = true;
281 }
282 };
283
284 std::barrier sync( std::ptrdiff_t( p ) , next_round );
285 run_workers( f_ex , p , [ & ]( unsigned i ) {
286 auto w = detail::bb_worker( f_p , i );
287 for( ;; ) {
288 sync.arrive_and_wait();
289 if( done )
290 return;
291 for( std::size_t j = i ; j < nslots ; j += p ) {
292 Slot & s = slots[ j ];
293 try {
294 detail::bb_process< P >( s.it.node , s.it.bound , s.it.depth , w ,
295 [ & ] { return( snapshot ); } ,
296 [ & ]( Value v , const Node & n ) {
297 if( ( v >= snapshot ) ||
298 ( s.best && ( v >= s.best->first ) ) )
299 return( false );
300 s.best.emplace( v , n );
301 return( true );
302 } ,
303 [ & ]( Node && c , Value b , unsigned d ) {
304 s.kids.push_back( Item{ std::move( c ) , b ,
305 d } );
306 } , o , s.st , s.fr );
307 }
308 catch( ... ) {
309 std::lock_guard< std::mutex > guard( error_m );
310 if( ! error )
311 error = std::current_exception();
312 }
313 }
314 }
315 } );
316 if( error )
317 std::rethrow_exception( error );
318
319 fr.add( q , BBLeafKind::open ); // left open by a limit
320 detail::bb_finish< P >( sol , inc , fr , limit , late );
321 st.seconds = clock.seconds();
322 return( sol );
323 }
324
325/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
326 // adds the counters of b to those of a, but for evaluated_by
327
328 static void add_counters( BBStats & a , const BBStats & b ) {
329 a.evaluated += b.evaluated;
330 a.branched += b.branched;
331 a.generated += b.generated;
332 a.infeasible += b.infeasible;
333 a.pruned_generated += b.pruned_generated;
334 a.pruned_extracted += b.pruned_extracted;
335 a.pruned_evaluated += b.pruned_evaluated;
336 a.incumbents += b.incumbents;
337 }
338
339/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
340
341 const P & f_p;
342 Options f_options;
343 Executor * f_ex = nullptr;
344
345 }; // end( class( DeterministicBranchAndBound ) )
346
347/*--------------------------------------------------------------------------*/
348
349} // end( namespace PBeFS_di_unipi_it )
350
351/*--------------------------------------------------------------------------*/
352
353#endif /* __PBeFS_DeterministicBranchAndBound */
354
355/*--------------------------------------------------------------------------*/
356/*----------------- End File DeterministicBranchAndBound.h -----------------*/
357/*--------------------------------------------------------------------------*/
Header file for the parallel best-first searches with an incumbent, i.e., the parallel Branch-and-Bou...
BasicParallelBBOptions< Value > Options
the options
Definition DeterministicBranchAndBound.h:129
typename P::node_type Node
the node type
Definition DeterministicBranchAndBound.h:127
BBLeaf< Node, Value > Leaf
the leaf type
Definition DeterministicBranchAndBound.h:130
DeterministicBranchAndBound(const P &problem, const Options &options={})
constructor, taking the problem (which must outlive the search)
Definition DeterministicBranchAndBound.h:137
SolutionT solve(std::vector< Leaf > seeds) const
searches the subtrees of the given nodes, returning the Solution [see BranchAndBoundSearch::solve()]
Definition DeterministicBranchAndBound.h:161
SolutionT solve(void) const
searches the tree from the root, returning the Solution
Definition DeterministicBranchAndBound.h:153
BBSolution< Node, Value > SolutionT
the solution type
Definition DeterministicBranchAndBound.h:131
void set_executor(Executor &ex) noexcept
sets the Executor that runs the workers, which must outlive the searches; without it,...
Definition DeterministicBranchAndBound.h:148
bb_value_t< P > Value
the value type
Definition DeterministicBranchAndBound.h:128
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
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 run_workers(Executor *ex, unsigned p, F &&f)
runs the workers f( 0 ) , ... , f( p - 1 ) at once on an Executor
Definition SearchUtils.h:121
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
BBStats stats
the counters
Definition BranchAndBound.h:479
the counters of a B&B
Definition BranchAndBound.h:417
std::vector< std::uint64_t > evaluated_by
evaluations per worker
Definition BranchAndBound.h:431
static constexpr V infinity(void) noexcept
the infinite value
Definition BranchAndBound.h:212
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