PBeFS
Parallel Best-First Search
Loading...
Searching...
No Matches
FarmAStar.h
Go to the documentation of this file.
1/*--------------------------------------------------------------------------*/
2/*---------------------------- File FarmAStar.h ----------------------------*/
3/*--------------------------------------------------------------------------*/
51/*--------------------------------------------------------------------------*/
52/*------------------------------ DEFINITIONS -------------------------------*/
53/*--------------------------------------------------------------------------*/
54
55#ifndef __PBeFS_FarmAStar
56 #define __PBeFS_FarmAStar
57 /* self-identification: #endif at the end of the file */
58
59/*--------------------------------------------------------------------------*/
60/*-------------------------------- INCLUDES --------------------------------*/
61/*--------------------------------------------------------------------------*/
62
63#include <memory>
64
65#include <stdexcept>
66
67#include <tuple>
68
69#include <type_traits>
70
71#include <vector>
72
73#include "PBeFS/astar/HDAStar.h"
74
75#include "ADS/parallel/Farm.h"
76
77/*--------------------------------------------------------------------------*/
78/*------------------------------- NAMESPACE --------------------------------*/
79/*--------------------------------------------------------------------------*/
80
82namespace PBeFS_di_unipi_it
83{
84
85/*--------------------------------------------------------------------------*/
86/*---------------------------- CLASS FarmAStar -----------------------------*/
87/*--------------------------------------------------------------------------*/
89
108
109template< Domain D , ADS::WorkerExecutor Executor = ADS::ThreadPool ,
110 class Open = std::conditional_t<
111 std::is_integral_v< cost_t< D > > ,
115
116/*------------------------ PUBLIC PART OF THE CLASS ------------------------*/
117
118 public:
119
120/*------------------------------ PUBLIC TYPES ------------------------------*/
121
126
127/*------------------------------ CONSTRUCTOR -------------------------------*/
128
131
132 explicit FarmAStar( const D & domain , const ParallelOptions & options = {} )
133 : f_domain( domain ) , f_options( options ) {
134 f_options.normalize();
135 }
136
137/*------------------------- OTHER INITIALIZATIONS --------------------------*/
141
142 void set_executor( Executor & ex ) noexcept { f_ex = &ex; }
143
144/*-------------------------------- SOLVING ---------------------------------*/
146
147 [[nodiscard]] SolutionT solve( void ) const
148 {
149 Stopwatch clock;
150 const unsigned w = f_options.threads;
151 Master master( *this );
152 std::vector< char > pinned( w , 0 );
153 const auto start = [ & ]( auto && emit , std::size_t ) {
154 if( ! ADS::isRunCaller() )
155 pin_worker( 0 );
156 master.start( emit );
157 };
158 const auto back = [ & ]( Task * t , auto && emit ) {
159 return( master.back( t , emit ) );
160 };
161 const auto work = [ & ]( Task * t , std::size_t i ) {
162 if( ! pinned[ i ] ) { // each index on one thread
163 pinned[ i ] = 1;
164 if( ! ADS::isRunCaller() )
165 pin_worker( unsigned( i + 1 ) );
166 }
167 expand( t );
168 return( t );
169 };
170 if( f_ex )
171 ADS::feedbackFarm< Task * , Task * >( *f_ex , w , start , back , work );
172 else {
173 Executor ex( w + 1 );
174 ADS::feedbackFarm< Task * , Task * >( ex , w , start , back , work );
175 }
176
177 SolutionT sol;
178 sol.stats = master.f_st;
179 sol.stats.stored = master.f_pool.size();
180 sol.stats.expanded_by = { sol.stats.expanded };
181 sol.stats.stored_by = { sol.stats.stored };
182 if( master.f_limit_hit )
184 else
185 if( master.f_goal ) {
187 sol.cost = master.f_goal->g;
188 sol.path = trace_path( master.f_goal );
189 }
190 sol.stats.seconds = clock.seconds();
191 return( sol );
192 }
193
194/*----------------------- PRIVATE PART OF THE CLASS ------------------------*/
195
196 private:
197
198 // a task: nodes to expand, and then their successors
199 struct Task {
200 std::vector< NodeT * > nodes; // the nodes
201 std::vector< std::uint8_t > ops; // their operators, when sent
202 std::vector< std::tuple< std::size_t , State , Cost , Cost ,
203 std::uint8_t > > succ;
204 // per successor: its node, state, edge cost, h and operator
205 };
206
207/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
208 // the master: OPEN, CLOSED, the nodes and the counters
209
210 struct Master {
211
212 explicit Master( const FarmAStar & a ) : f_a( a ) {}
213
214 Master( const Master & ) = delete;
215 Master & operator=( const Master & ) = delete;
216
217 ~Master() {
218 for( Task * t : v_free )
219 delete t;
220 }
221
222 // the start: the root, and the first tasks
223 template< class E >
224 void start( E && emit ) {
225 const State s0 = f_a.f_domain.initial_state();
226 insert( s0 , Cost( 0 ) , Cost( f_a.f_domain.heuristic( s0 ) ) ,
227 nullptr , no_op , f_a.f_domain.hash( s0 ) );
228 dispatch( emit );
229 }
230
231 // a task back, with the successors of its nodes; false once nothing is
232 // out at the workers and nothing is left to send
233 template< class E >
234 bool back( Task * t , E && emit ) {
235 --f_out;
236 v_hash.resize( t->succ.size() ); // prefetch the batch first
237 for( std::size_t i = 0 ; i < t->succ.size() ; ++i ) {
238 v_hash[ i ] = f_a.f_domain.hash( std::get< 1 >( t->succ[ i ] ) );
239 f_table.prefetch( v_hash[ i ] );
240 }
241 for( std::size_t i = 0 ; i < t->succ.size() ; ++i ) {
242 const auto & [ k , s , c , h , o ] = t->succ[ i ];
243 ++f_st.generated;
244 NodeT * parent = t->nodes[ k ];
245 const Cost g = parent->g + c;
246 if( g + h < f_incumbent )
247 insert( s , g , h , parent , o , v_hash[ i ] );
248 }
249 if( f_a.f_options.node_limit &&
250 ( f_pool.size() >= f_a.f_options.node_limit ) )
251 f_limit_hit = true;
252 v_free.push_back( t );
253 dispatch( emit );
254 return( ( f_out > 0 ) || ( ! f_limit_hit && has_work() ) );
255 }
256
257 // sends tasks while there are nodes and free workers
258 template< class E >
259 void dispatch( E && emit ) {
260 while( ! f_limit_hit && ( f_out < f_a.f_options.threads ) ) {
261 Task * t = nullptr;
262 while( NodeT * n = next() ) {
263 n->closed = true;
264 ++f_st.expanded;
265 if( n->expanded )
266 ++f_st.reexpanded;
267 n->expanded = true;
268 if( f_a.f_domain.is_goal( n->state ) ) {
269 if( n->g < f_incumbent ) {
270 f_incumbent = n->g;
271 f_goal = n;
272 }
273 continue;
274 }
275 if( ! t ) {
276 if( v_free.empty() )
277 t = new Task;
278 else {
279 t = v_free.back();
280 v_free.pop_back();
281 }
282 t->nodes.clear();
283 t->ops.clear();
284 t->succ.clear();
285 }
286 t->nodes.push_back( n );
287 t->ops.push_back( n->op );
288 if( t->nodes.size() >= f_a.f_options.batch )
289 break;
290 }
291 if( ! t )
292 return;
293 ++f_out;
294 emit( t );
295 }
296 }
297
298 // true if OPEN has a node with f below the incumbent
299 bool has_work( void ) {
300 while( ! f_open.empty() && f_open.top().stale() )
301 f_open.pop();
302 return( ! f_open.empty() && ( f_open.top_f() < f_incumbent ) );
303 }
304
305 // the best node of OPEN with f below the incumbent, removed, or nullptr
306 NodeT * next( void ) {
307 return( has_work() ? f_open.pop().node : nullptr );
308 }
309
310 // the path to s, whose hash is hash, of cost g through parent and op
311 void insert( const State & s , Cost g , Cost h , NodeT * parent ,
312 std::uint8_t op , std::uint64_t hash ) {
313 auto [ x , fresh ] = f_table.find_or_insert( s , hash , [ & ] {
314 NodeT * y = f_pool.allocate();
315 *y = NodeT{ s , g , h , parent , false , false , op };
316 return( y );
317 } );
318 if( fresh )
319 f_open.push( x );
320 else
321 if( g < x->g ) {
322 x->g = g;
323 x->parent = parent;
324 x->op = op;
325 x->closed = false;
326 f_open.push( x );
327 }
328 else
329 ++f_st.duplicates;
330 }
331
332 const FarmAStar & f_a; // the search
333 NodePool< NodeT > f_pool; // the nodes
334 NodeTable< NodeT > f_table; // CLOSED
335 Open f_open; // OPEN
336 std::vector< Task * > v_free; // tasks to reuse
337 std::vector< std::uint64_t > v_hash; // hashes of a task back
338 unsigned f_out = 0; // tasks at the workers
339 Cost f_incumbent = infinite_cost< Cost >(); // the best cost found
340 NodeT * f_goal = nullptr; // the best goal found
341 bool f_limit_hit = false; // the node limit is hit
342 SearchStats f_st; // the counters
343 };
344
345/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
346 // the work of a worker: the successors of the nodes of a task
347
348 void expand( Task * t ) const {
349 for( std::size_t k = 0 ; k < t->nodes.size() ; ++k ) {
350 const NodeT * n = t->nodes[ k ];
351 for_each_child( f_domain , n->state , n->h , t->ops[ k ] ,
352 [ & ]( const State & s , Cost c , Cost h ,
353 std::uint8_t o ) {
354 t->succ.emplace_back( k , s , c , h , o );
355 } );
356 }
357 }
358
359/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
360
361 const D & f_domain;
362 ParallelOptions f_options;
363 Executor * f_ex = nullptr;
364
365 }; // end( class( FarmAStar ) )
366
367/*--------------------------------------------------------------------------*/
368
369} // end( namespace PBeFS_di_unipi_it )
370
371/*--------------------------------------------------------------------------*/
372
373#endif /* __PBeFS_FarmAStar */
374
375/*--------------------------------------------------------------------------*/
376/*-------------------------- End File FarmAStar.h --------------------------*/
377/*--------------------------------------------------------------------------*/
Header file for the class template HDAStar, Hash Distributed A* on a shared-memory machine,...
the OPEN list on the BucketQueue of ADS, for integer costs
Definition OpenList.h:122
state_t< D > State
the state type
Definition FarmAStar.h:122
Solution< State, Cost > SolutionT
the solution type
Definition FarmAStar.h:125
Node< State, Cost > NodeT
the node type
Definition FarmAStar.h:124
cost_t< D > Cost
the cost type
Definition FarmAStar.h:123
SolutionT solve(void) const
searches the Domain, returning the Solution
Definition FarmAStar.h:147
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 FarmAStar.h:142
FarmAStar(const D &domain, const ParallelOptions &options={})
constructor, taking the Domain (which must outlive the FarmAStar); threads is the number of workers,...
Definition FarmAStar.h:132
the OPEN list on the DaryHeap of ADS
Definition OpenList.h:52
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
a search domain
Definition Domain.h:103
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
@ 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
typename D::state_type state_t
the state type of a Domain
Definition Domain.h:241
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
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
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
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
std::vector< std::uint64_t > expanded_by
expansions of each worker
Definition Solution.h:75
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::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
std::vector< State > path
the states along the path
Definition Solution.h:104
Cost cost
the cost of the path
Definition Solution.h:103
SearchStats stats
the counters of the search
Definition Solution.h:105
Status status
how the search ended
Definition Solution.h:102