58#ifndef __PBeFS_MQAStar
59 #define __PBeFS_MQAStar
86#include "ADS/concurrent/MultiQueue.h"
125template< Domain D , ADS::WorkerExecutor Executor = ADS::ThreadPool >
144 : f_domain( domain ) , f_options( options ) {
160 const unsigned p = f_options.threads;
162 Shared sh( f_domain , p , f_options.seed );
163 std::vector< std::unique_ptr< Worker > > w( p );
164 for(
unsigned i = 0 ; i < p ; ++i )
165 w[ i ] = std::make_unique< Worker >( *
this , sh );
168 const State s0 = f_domain.initial_state();
169 NodeT * root = w[ 0 ]->update( s0 ,
Cost( 0 ) ,
170 Cost( f_domain.heuristic( s0 ) ) ,
172 auto hd = sh.open.handle();
176 run_workers( f_ex , p , [ & ](
unsigned i ) { w[ i ]->run(); } );
180 for(
auto & x : w ) {
185 st.
stored += x->f_local.stored();
187 st.
stored_by.push_back( x->f_local.stored() );
189 if( sh.limit_hit.load() )
194 sol.cost = sh.goal->
g;
205 using Lock =
typename Executor::lock_type;
212 static constexpr bool kPacked = std::is_integral_v< Cost >;
213 using Key = std::conditional_t< kPacked , std::uint64_t , Cost >;
216 if constexpr( kPacked )
222 static Cost f_of( Key k ) {
223 if constexpr( kPacked )
224 return(
Cost( k >> 32 ) );
230 using Mq = ADS::MultiQueue< Key , OpenEntry< NodeT > >;
236 Shared(
const D & d ,
unsigned p , std::uint64_t seed )
237 : open( 2 * std::size_t( p ) , 1 , seed ) , closed( d ) {}
241 alignas(
cache_line ) std::atomic< std::size_t > pending{ 1 };
243 alignas(
cache_line ) std::atomic< Cost > incumbent{
245 std::atomic< bool > done{
false };
246 std::atomic< bool > limit_hit{
false };
247 std::mutex goal_mutex;
248 NodeT * goal =
nullptr;
254 struct alignas( cache_line ) Worker {
256 Worker(
const MQAStar & a , Shared & s ) : f_a( a ) , f_sh( s ) {}
260 ADS::Backoff backoff;
261 auto hd = f_sh.open.handle();
262 std::vector< OpenEntry< NodeT > > fresh;
263 while( ! f_sh.done.load( std::memory_order_acquire ) ) {
264 const auto e = hd.tryPop();
266 if( f_sh.pending.load( std::memory_order_acquire ) == 0 ) {
267 f_sh.done.store(
true , std::memory_order_release );
275 if( f_of( e->key ) < f_sh.incumbent.load() )
276 expand( e->item , fresh );
279 if( fresh.size() != 1 )
280 f_sh.pending.fetch_add( fresh.size() - 1 , std::memory_order_acq_rel );
281 auto entries = fresh | std::views::transform(
282 [](
const OpenEntry< NodeT > & x ) {
283 return(
typename Mq::Entry{
284 key( x.g , x.node->h ) , x } ); } );
285 hd.pushAll( entries.begin() , entries.end() );
291 void expand(
const OpenEntry< NodeT > & e ,
292 std::vector< OpenEntry< NodeT > > & fresh ) {
298 std::lock_guard< Lock > guard( f_sh.closed.lock( n ) );
312 if( f_a.f_domain.is_goal( s ) ) {
313 std::lock_guard< std::mutex > guard( f_sh.goal_mutex );
314 if( g < f_sh.incumbent.load() ) {
315 f_sh.incumbent.store( g );
329 const Cost gt = g + c;
330 if( gt + ht >= f_sh.incumbent.load( std::memory_order_relaxed ) )
332 const std::uint64_t hash = f_a.f_domain.hash( t );
333 f_sh.closed.prefetch( hash );
334 f_succ.push_back( { t , gt , ht , hash , ot } );
336 for(
const auto & v : f_succ )
337 if(
NodeT * x = f_sh.closed.update( v.state , v.hash , v.g , v.h , n ,
338 v.op , f_local , f_st.duplicates ) )
339 fresh.push_back( OpenEntry<
NodeT >{ x , v.g } );
341 if( f_a.f_options.node_limit &&
342 ( f_local.stored() >= f_a.f_options.node_limit ) ) {
343 f_sh.limit_hit.store( true );
344 f_sh.done.store( true , std::memory_order_release );
352 return( f_sh.closed.update( t , f_a.f_domain.hash( t ) , g , h ,
353 parent , op , f_local , f_st.duplicates ) );
359 std::vector< typename Closed::Successor > f_succ;
367 Executor * f_ex =
nullptr;
Header file for the class template ConcurrentClosed, the closed list that the workers of SPAStar and ...
Header file for the class template HDAStar, Hash Distributed A* on a shared-memory machine,...
the closed list shared by the workers, a lock-free set of the nodes
Definition ConcurrentClosed.h:76
state_t< D > State
the state type
Definition MQAStar.h:134
Solution< State, Cost > SolutionT
the solution type
Definition MQAStar.h:137
Node< State, Cost > NodeT
the node type
Definition MQAStar.h:136
cost_t< D > Cost
the cost type
Definition MQAStar.h:135
MQAStar(const D &domain, const ParallelOptions &options={})
constructor, taking the Domain (which must outlive the MQAStar)
Definition MQAStar.h:143
SolutionT solve(void) const
searches the Domain, returning the Solution
Definition MQAStar.h:158
void set_executor(Executor &ex) noexcept
sets the Executor that runs the workers, which must outlive the searches; without it,...
Definition MQAStar.h:153
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
void for_each_child(const D &d, const state_t< D > &s, cost_t< D > h, std::uint8_t op, F &&f)
the successors of s but its parent, when D knows the operators
Definition Domain.h:276
typename D::cost_type cost_t
the cost type of a Domain
Definition Domain.h:245
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
@ solved
an optimal path has been found
Definition Solution.h:47
@ node_limit
the limit on the stored nodes was reached first
Definition Solution.h:49
constexpr Cost infinite_cost(void) noexcept
the largest value of a cost type, used as "no incumbent yet"
Definition Node.h:100
constexpr std::uint64_t packed_fg(Cost g, Cost h) noexcept
with integer costs, f = g + h in the high half of a word and the complement of g in the low one,...
Definition Node.h:116
typename D::state_type state_t
the state type of a Domain
Definition Domain.h:241
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
constexpr std::uint8_t no_op
the operator of the root, which no state was generated by
Definition Domain.h:205
auto trace_path(const NodeT *goal)
the states from the root to goal, following the parent pointers
Definition Node.h:126
the nodes that a worker allocates, and its spare node
Definition ConcurrentClosed.h:88
a node of the search graph
Definition Node.h:74
Cost g
the cost of the best path found from the root
Definition Node.h:77
Cost h
the heuristic estimate of the cost to a goal
Definition Node.h:78
an entry of the OPEN list: the node and the g it had when pushed
Definition OpenList.h:73
the options of the parallel searches: those of the sequential search, and the parameters of the paral...
Definition HDAStar.h:114
void normalize(void) noexcept
sets to 1 any parameter that is 0
Definition SearchUtils.h:102
the counters of a search
Definition Solution.h:64
std::vector< std::uint64_t > expanded_by
expansions of each worker
Definition Solution.h:75
std::uint64_t generated
successors generated
Definition Solution.h:68
std::uint64_t reexpanded
expansions of a node already expanded
Definition Solution.h:67
std::uint64_t stored
distinct nodes stored at the end
Definition Solution.h:73
double seconds
wall-clock time of the search
Definition Solution.h:74
std::uint64_t duplicates
generated states already known with a not larger
Definition Solution.h:69
std::vector< std::uint64_t > stored_by
nodes stored by each worker
Definition Solution.h:76
std::uint64_t expanded
expansions, re-expansions included
Definition Solution.h:66
a path from the initial state to a goal, and its cost
Definition Solution.h:100