114 static_assert( std::is_trivially_copyable_v< state_t< D > > ,
115 "HDAStarMPI: the states have to be trivially copyable" );
133 MPI_Comm comm = MPI_COMM_WORLD )
134 : f_domain( domain ) , f_dist( dist ) , f_options( options ) ,
177 [[no_unique_address]] OperatorOf< D > o;
187 using Mail = std::variant< Msg , Incumbent >;
190 using Open_t = std::conditional_t< std::is_integral_v< Cost > ,
191 BucketOpenList< MNode > ,
192 HeapOpenList< MNode > >;
200 : f_a( a ) , f_comm( a.f_comm ) ,
201 f_mail( a.f_comm , a.f_options.batch ) , f_dist( a.f_dist ) {
202 MPI_Comm_rank( f_comm , & f_rank );
204 MPI_Comm_size( f_comm , & np );
205 f_p = unsigned( np );
210 const State s0 = f_a.f_domain.initial_state();
211 if( f_dist.owner( s0 , f_p ) ==
unsigned( f_rank ) )
212 insert( Msg{ s0 ,
Cost( 0 ) ,
Cost( f_a.f_domain.heuristic( s0 ) ) , s0 ,
214 f_a.f_domain.hash( s0 ) );
216 unsigned since_flush = 0;
219 if( MNode * n = next() ) {
221 if( ++since_flush >= f_a.f_options.flush_period ) {
225 if( f_a.f_options.node_limit && ! f_mail.stopped() &&
226 ( f_pool.size() >= f_a.f_options.node_limit ) ) {
232 if( f_mail.quiescent() )
238 MNode * next_peek(
void ) {
239 if( f_mail.stopped() )
241 while( ! f_open.empty() ) {
242 const OpenEntry< MNode > e = f_open.top();
243 if( e.node->closed || ( e.node->g != e.g ) ) {
247 if( e.node->g + e.node->h >= f_incumbent )
255 MNode * next(
void ) {
256 MNode * n = next_peek();
263 void expand( MNode * n ) {
270 if( f_a.f_domain.is_goal( n->state ) ) {
271 if( n->g < f_incumbent ) {
274 for(
unsigned q = 0 ; q < f_p ; ++q )
275 if( q !=
unsigned( f_rank ) )
276 f_mail.send( q , Mail( Incumbent{ n->g } ) );
288 const Cost g = n->g + c;
289 if( g + ht >= f_incumbent )
291 const auto [ dst , key ] = owner( t );
292 const Msg m{ t , g , ht , n->state , true , ot , key };
293 if( dst ==
unsigned( f_rank ) ) {
294 insert( m , f_a.f_domain.hash( t ) );
297 f_mail.send( dst , Mail( m ) );
304 void insert(
const Msg & m , std::uint64_t h ) {
305 if( f_mail.stopped() || ( m.g + m.h >= f_incumbent ) )
307 auto [ x , fresh ] = f_table.find_or_insert( m.s , h , [ & ] {
308 MNode * y = f_pool.allocate();
309 *y = MNode{ m.s , m.g , m.h , m.parent , m.has_parent , false , false ,
318 x->parent = m.parent;
319 x->has_parent = m.has_parent;
330 void receive(
void ) {
332 f_mail.poll( [ & ](
const Mail & x ) {
333 if(
const Msg * m = std::get_if< Msg >( & x ) )
334 v_msg.push_back( *m );
336 f_incumbent = std::min( f_incumbent , std::get< Incumbent >( x ).c );
338 const std::size_t k = v_msg.size();
340 for( std::size_t i = 0 ; i < k ; ++i ) {
341 v_hash[ i ] = f_a.f_domain.hash( v_msg[ i ].s );
342 f_table.prefetch( v_hash[ i ] );
344 for( std::size_t i = 0 ; i < k ; ++i )
345 insert( v_msg[ i ] , v_hash[ i ] );
349 void gather( SolutionT & sol ) {
351 unsigned long long mine[ 7 ] = { f_st.expanded , f_st.reexpanded ,
352 f_st.generated , f_st.duplicates ,
353 f_st.sent , f_mail.batches() ,
355 std::vector< unsigned long long > all( 7 * std::size_t( f_p ) );
356 MPI_Allgather( mine , 7 , MPI_UNSIGNED_LONG_LONG , all.data() , 7 ,
357 MPI_UNSIGNED_LONG_LONG , f_comm );
358 for(
unsigned q = 0 ; q < f_p ; ++q ) {
359 const auto * x = all.data() + 7 * std::size_t( q );
360 st.expanded += x[ 0 ];
361 st.reexpanded += x[ 1 ];
362 st.generated += x[ 2 ];
363 st.duplicates += x[ 3 ];
365 st.messages += x[ 5 ];
367 st.expanded_by.push_back( x[ 0 ] );
368 st.stored_by.push_back( x[ 6 ] );
371 int limit = f_limit_hit ? 1 : 0 , any_limit = 0;
372 MPI_Allreduce( & limit , & any_limit , 1 , MPI_INT , MPI_MAX , f_comm );
378 struct {
double c;
int r; } best{ f_goal ? double( f_goal->g )
381 MPI_Allreduce( & best , & win , 1 , MPI_DOUBLE_INT , MPI_MINLOC ,
393 if( f_rank == win.r )
394 step = Step{ f_goal->state , f_goal->g , f_goal->parent ,
395 f_goal->has_parent };
396 MPI_Bcast( & step ,
int(
sizeof( Step ) ) , MPI_BYTE , win.r , f_comm );
399 std::vector< State > path{ step.s };
400 while( step.has_parent ) {
401 const State s = step.parent;
402 const int owner = int( f_dist.owner( s , f_p ) );
403 if( f_rank == owner ) {
404 const MNode * x = f_table.find( s , f_a.f_domain.hash( s ) );
406 throw( std::logic_error(
"HDAStarMPI::solve: lost node" ) );
407 step = Step{ x->state , x->g , x->parent , x->has_parent };
409 MPI_Bcast( & step ,
int(
sizeof( Step ) ) , MPI_BYTE , owner , f_comm );
410 path.push_back( step.s );
412 sol.path.assign( path.rbegin() , path.rend() );
417 ADS::MPIMailbox< Mail > f_mail;
424 std::vector< Msg > v_msg;
425 std::vector< std::uint64_t > v_hash;
427 MNode * f_goal =
nullptr;
428 bool f_limit_hit =
false;