PBeFS
Parallel Best-First Search
Loading...
Searching...
No Matches
DDDAStar.h
Go to the documentation of this file.
1/*--------------------------------------------------------------------------*/
2/*---------------------------- File DDDAStar.h -----------------------------*/
3/*--------------------------------------------------------------------------*/
71/*--------------------------------------------------------------------------*/
72/*------------------------------ DEFINITIONS -------------------------------*/
73/*--------------------------------------------------------------------------*/
74
75#ifndef __PBeFS_DDDAStar
76 #define __PBeFS_DDDAStar
77 /* self-identification: #endif at the end of the file */
78
79/*--------------------------------------------------------------------------*/
80/*-------------------------------- INCLUDES --------------------------------*/
81/*--------------------------------------------------------------------------*/
82
83#include <algorithm>
84
85#include <cstddef>
86
87#include <cstdint>
88
89#include <filesystem>
90
91#include <map>
92
93#include <memory>
94
95#include <set>
96
97#include <stdexcept>
98
99#include <string>
100
101#include <tuple>
102
103#include <type_traits>
104
105#include <utility>
106
107#include <vector>
108
109#include "ADS/dictionary/FlatHashTable.h"
110
111#include "ADS/external/RecordFile.h"
112
113#include "PBeFS/astar/Domain.h"
114
116
117#include "PBeFS/astar/Solution.h"
118
119/*--------------------------------------------------------------------------*/
120/*------------------------------- NAMESPACE --------------------------------*/
121/*--------------------------------------------------------------------------*/
122
124namespace PBeFS_di_unipi_it
125{
126
127/*--------------------------------------------------------------------------*/
128/*-------------------------------- CONCEPTS --------------------------------*/
129/*--------------------------------------------------------------------------*/
133
134template< class D >
135concept DiskDomain = Domain< D > && std::is_integral_v< cost_t< D > > &&
136 std::is_trivially_copyable_v< state_t< D > >;
137
138/*--------------------------------------------------------------------------*/
139/*--------------------------------- TYPES ----------------------------------*/
140/*--------------------------------------------------------------------------*/
142
146 std::size_t partitions = 64;
149 std::filesystem::path directory;
152 std::size_t buffer_bytes = std::size_t( 1 ) << 16;
153 }; // end( struct( DDDOptions ) )
154
155/*--------------------------------------------------------------------------*/
156/*---------------------------- CLASS DDDAStar ------------------------------*/
157/*--------------------------------------------------------------------------*/
159
165
166template< DiskDomain D >
167class DDDAStar {
168
169/*------------------------ PUBLIC PART OF THE CLASS ------------------------*/
170
171 public:
172
173/*------------------------------ PUBLIC TYPES ------------------------------*/
174
178
179/*------------------------------ CONSTRUCTOR -------------------------------*/
180
182
183 explicit DDDAStar( const D & domain , const SearchOptions & options = {} ,
184 DDDOptions ddd = {} )
185 : f_domain( domain ) , f_options( options ) , f_ddd( std::move( ddd ) )
186 {
187 if( f_ddd.partitions == 0 )
188 throw( std::invalid_argument( "DDDAStar: no partition" ) );
189 }
190
191/*-------------------------------- SOLVING ---------------------------------*/
193
194 [[nodiscard]] SolutionT solve( void ) const
195 {
196 Stopwatch clock;
197 Run r( f_domain , f_options , f_ddd );
198 SolutionT sol = r.search();
199 sol.stats = std::move( r.st );
200 sol.stats.expanded_by = { sol.stats.expanded };
201 sol.stats.stored_by = { sol.stats.stored };
202 sol.stats.seconds = clock.seconds();
203 return( sol );
204 }
205
206/*----------------------- PRIVATE PART OF THE CLASS ------------------------*/
207
208 private:
209
212 struct Rec {
213 State s;
214 std::uint64_t parent;
215 [[no_unique_address]] OperatorOf< D > o;
216 };
217
219 struct Val {
220 std::uint64_t parent;
221 [[no_unique_address]] OperatorOf< D > o;
222 };
223
225 struct Hash {
226 const D * d;
227 std::uint64_t operator()( const State & s ) const {
228 return( d->hash( s ) );
229 }
230 };
231
232 using Table = ADS::FlatHashTable< State , Val , Hash >;
233
235 struct Key {
236 Cost f;
237 Cost g;
238 bool operator<( const Key & o ) const {
239 return( ( f < o.f ) || ( ( f == o.f ) && ( g < o.g ) ) );
240 }
241 };
242
244 struct Run {
245
246 const D & d;
247 const SearchOptions & opt;
248 const DDDOptions & ddd;
249 ADS::TempDirectory dir;
250 std::size_t buffer;
251 std::set< Key > open;
252 std::map< Cost , std::vector< Cost > > closed;
254 SearchStats st;
255
256 Run( const D & domain , const SearchOptions & o , const DDDOptions & x )
257 : d( domain ) , opt( o ) , ddd( x ) , dir( x.directory , "pbefs-ddd-" ) ,
258 buffer( std::max< std::size_t >( 1 , x.buffer_bytes / sizeof( Rec ) ) )
259 {}
260
261 // the file p of the open (o) or closed (c) bucket ( g , h )
262 [[nodiscard]] ADS::RecordFile< Rec > file( char kind , Cost g , Cost h ,
263 std::size_t p ) const {
264 return( ADS::RecordFile< Rec >( dir / ( std::string( 1 , kind ) + "_" +
265 std::to_string( g ) + "_" +
266 std::to_string( h ) + "_" +
267 std::to_string( p ) ) ) );
268 }
269
270 [[nodiscard]] std::size_t part( const State & s ) const {
271 return( std::size_t( d.hash( s ) % ddd.partitions ) );
272 }
273
274 SolutionT search( void ) {
275 SolutionT sol;
276 const State s0 = d.initial_state();
277 const Cost h0 = d.heuristic( s0 );
278 {
279 const Rec root{ s0 , d.hash( s0 ) , {} };
280 file( 'o' , Cost( 0 ) , h0 , part( s0 ) ).append( &root , 1 );
281 open.insert( Key{ h0 , Cost( 0 ) } );
282 }
283
284 while( ! open.empty() ) {
285 const Key k = *open.begin();
286 open.erase( open.begin() );
287 const Cost g = k.g , h = k.f - k.g;
288 // the files of the buckets ( g' , h' ) that the successors go to, by
289 // ( g' , h' , p ), with their writers, kept for this bucket only
290 std::map< std::tuple< Cost , Cost , std::size_t > ,
291 std::pair< std::unique_ptr< ADS::RecordFile< Rec > > ,
292 std::unique_ptr< ADS::RecordWriter< Rec > > > > out;
293 const auto writer = [ & ]( Cost gv , Cost hv , std::size_t p )
294 -> ADS::RecordWriter< Rec > & {
295 auto & slot = out[ { gv , hv , p } ];
296 if( ! slot.second ) {
297 slot.first = std::make_unique< ADS::RecordFile< Rec > >(
298 file( 'o' , gv , hv , p ) );
299 slot.second = std::make_unique< ADS::RecordWriter< Rec > >(
300 *slot.first , buffer );
301 }
302 return( *slot.second );
303 };
304 for( std::size_t p = 0 ; p < ddd.partitions ; ++p ) {
305 auto in = file( 'o' , g , h , p );
306 if( in.empty() ) {
307 in.remove();
308 continue;
309 }
310 Table t( in.size() , Hash{ &d } );
311 in.forEach( [ & ]( const Rec & x ) {
312 if( ! t.insert( x.s , Val{ x.parent , x.o } ) )
313 ++st.duplicates; // a copy in the file
314 } );
315 in.remove();
316 const auto cl = closed.find( h );
317 if( cl != closed.end() )
318 for( Cost gc : cl->second )
319 file( 'c' , gc , h , p ).forEach( [ & ]( const Rec & x ) {
320 if( t.remove( x.s ) )
321 ++st.duplicates; // closed already
322 } );
323 if( t.empty() )
324 continue;
325
326 std::vector< Rec > fresh; // the closed file p
327 fresh.reserve( t.size() );
328 t.forEach( [ & ]( const State & s , const Val & v ) {
329 fresh.push_back( Rec{ s , v.parent , v.o } );
330 } );
331 file( 'c' , g , h , p ).append( fresh.data() , fresh.size() );
332 st.stored += fresh.size();
333 closed[ h ]; // the list of this h
334
335 for( const Rec & x : fresh )
336 if( d.is_goal( x.s ) ) {
337 mark_closed( g , h );
338 sol.status = Status::solved;
339 sol.cost = g;
340 sol.path = trace( x , g );
341 return( sol );
342 }
343
344 for( const Rec & x : fresh ) {
345 ++st.expanded;
346 const std::uint64_t hu = d.hash( x.s );
347 for_each_child( d , x.s , h , x.o.op , [ & ]( const State & v , Cost c ,
348 Cost hv ,
349 std::uint8_t ov ) {
350 ++st.generated;
351 if( c <= Cost( 0 ) )
352 throw( std::logic_error( "DDDAStar: an edge of a cost that is not"
353 " positive" ) );
354 if( g + c + hv < k.f )
355 throw( std::logic_error( "DDDAStar: the heuristic is not"
356 " consistent" ) );
357 writer( g + c , hv , part( v ) ).push( Rec{ v , hu , ov } );
358 } );
359 }
360
361 if( opt.node_limit && ( st.stored >= opt.node_limit ) ) {
362 mark_closed( g , h );
363 sol.status = Status::node_limit;
364 return( sol );
365 }
366 }
367
368 for( auto & [ key , fw ] : out ) {
369 fw.second->flush();
370 const auto [ gv , hv , p ] = key;
371 open.insert( Key{ gv + hv , gv } );
372 }
373 mark_closed( g , h );
374 }
375 sol.status = Status::unsolvable;
376 return( sol );
377 }
378
379 // the bucket ( g , h ) among the closed ones of h, if it has a file
380 void mark_closed( Cost g , Cost h ) {
381 auto it = closed.find( h );
382 if( ( it != closed.end() ) &&
383 ( std::find( it->second.begin() , it->second.end() , g ) ==
384 it->second.end() ) )
385 it->second.push_back( g );
386 }
387
388 // the path from s0 to the goal x, of cost g: each parent
389 // is looked for in the closed files of its partition, among the states
390 // of smaller g whose hash is the one recorded and that have the child as
391 // a successor at the cost of the difference
392 std::vector< State > trace( Rec x , Cost g ) {
393 std::vector< State > path{ x.s };
394 const State s0 = d.initial_state();
395 while( ! ( ( g == Cost( 0 ) ) && ( x.s == s0 ) ) ) {
396 const std::size_t p = std::size_t( x.parent % ddd.partitions );
397 bool found = false;
398 for( const auto & e : closed ) {
399 const Cost hc = e.first;
400 for( Cost gc : e.second ) {
401 if( gc >= g )
402 continue;
403 file( 'c' , gc , hc , p ).forEach( [ & ]( const Rec & y ) {
404 if( found || ( d.hash( y.s ) != x.parent ) )
405 return;
406 d.for_each_successor( y.s , hc , [ & ]( const State & v , Cost c ,
407 Cost ) {
408 if( ! found && ( v == x.s ) && ( gc + c == g ) ) {
409 found = true;
410 x = y;
411 g = gc;
412 }
413 } );
414 } );
415 if( found )
416 break;
417 }
418 if( found )
419 break;
420 }
421 if( ! found )
422 throw( std::logic_error( "DDDAStar: the parent of a state is not in"
423 " the closed files" ) );
424 path.push_back( x.s );
425 }
426 std::reverse( path.begin() , path.end() );
427 return( path );
428 }
429
430 }; // end( struct( Run ) )
431
432/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
433
434 const D & f_domain;
435 SearchOptions f_options;
436 DDDOptions f_ddd;
437
438 }; // end( class( DDDAStar ) )
439
440/*--------------------------------------------------------------------------*/
441/*------------------------------- FUNCTIONS --------------------------------*/
442/*--------------------------------------------------------------------------*/
444
445template< DiskDomain D >
447 const D & domain , const SearchOptions & options = {} ,
448 const DDDOptions & ddd = {} )
449{
450 return( DDDAStar< D >( domain , options , ddd ).solve() );
451 }
452
453/*--------------------------------------------------------------------------*/
454
455} // end( namespace PBeFS_di_unipi_it )
456
457/*--------------------------------------------------------------------------*/
458
459#endif /* __PBeFS_DDDAStar */
460
461/*--------------------------------------------------------------------------*/
462/*-------------------------- End File DDDAStar.h ---------------------------*/
463/*--------------------------------------------------------------------------*/
Header file for the concepts a search problem has to satisfy in order to be searched by the algorithm...
Header file for the small helpers shared by the searches: a stopwatch, the placement of the workers o...
Header file for the outcome of a search: the class template Solution, the statistics SearchStats coll...
DDDAStar(const D &domain, const SearchOptions &options={}, DDDOptions ddd={})
constructor, taking the Domain (which must outlive the DDDAStar)
Definition DDDAStar.h:183
state_t< D > State
the state type
Definition DDDAStar.h:175
Solution< State, Cost > SolutionT
the solution type
Definition DDDAStar.h:177
cost_t< D > Cost
the cost type
Definition DDDAStar.h:176
SolutionT solve(void) const
searches the Domain, returning the Solution
Definition DDDAStar.h:194
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 Domain that DDDAStar can search: integer costs, since a bucket is a pair of values,...
Definition DDDAStar.h:135
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
@ unsolvable
OPEN emptied without reaching a goal.
Definition Solution.h:48
@ 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
typename D::state_type state_t
the state type of a Domain
Definition Domain.h:241
Solution< state_t< D >, cost_t< D > > ddd_astar(const D &domain, const SearchOptions &options={}, const DDDOptions &ddd={})
searches a Domain with A* and delayed duplicate detection on the disk
Definition DDDAStar.h:446
the options of DDDAStar, besides those of all the searches
Definition DDDAStar.h:143
std::size_t partitions
the files of a bucket, i.e., the parts by the hash of the state: the largest bucket over this number ...
Definition DDDAStar.h:146
std::size_t buffer_bytes
the bytes of the buffer of each file being written, i.e., of each file of the buckets that the succes...
Definition DDDAStar.h:152
std::filesystem::path directory
where the files go, in a directory of their own removed at the end; the temporary directory of the sy...
Definition DDDAStar.h:149
Definition Domain.h:260
the options shared by all the searches
Definition Solution.h:119
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
SearchStats stats
the counters of the search
Definition Solution.h:105