00001 //----------------------------------------------------------------------------- 00002 /** @file fringesearch.h 00003 IDA* like search keeping a list of the fringe nodes. 00004 00005 $Id: fringesearch_8h-source.html,v 1.1.1.1 2003/08/07 19:41:39 emarkus Exp $ 00006 $Source: /usr/cvsroot/www_pathfind/libpathfind/0.1.0/doc/fringesearch_8h-source.html,v $ 00007 */ 00008 //----------------------------------------------------------------------------- 00009 00010 #ifndef PATHFIND_FRINGESEARCH_H 00011 #define PATHFIND_FRINGESEARCH_H 00012 00013 #include <vector> 00014 #include "marker.h" 00015 #include "search.h" 00016 00017 //----------------------------------------------------------------------------- 00018 00019 namespace PathFind 00020 { 00021 using namespace std; 00022 00023 /** Statically allocated linked list used in FringeSearch. */ 00024 class Fringe 00025 { 00026 public: 00027 Fringe(); 00028 00029 int getCurrentNode() const 00030 { 00031 return m_currentNode; 00032 } 00033 00034 void init(int numberNodes); 00035 00036 void insertNode(int nodeId); 00037 00038 void nextNode(); 00039 00040 void print(ostream& o) const; 00041 00042 void removeCurrentNode() 00043 { 00044 removeNode(m_currentNode); 00045 } 00046 00047 void startIteration(); 00048 00049 private: 00050 /** Node used in Fringe. */ 00051 class Node 00052 { 00053 public: 00054 int m_next; 00055 00056 int m_previous; 00057 }; 00058 00059 00060 int m_currentNode; 00061 00062 int m_numberNodes; 00063 00064 int m_headNode; 00065 00066 vector<bool> m_isInList; 00067 00068 vector<Node> m_nodes; 00069 00070 00071 Node& getNode(int nodeId) 00072 { 00073 assert(nodeId >= 0 && nodeId < m_numberNodes); 00074 return m_nodes[nodeId]; 00075 } 00076 00077 const Node& getNode(int nodeId) const 00078 { 00079 assert(nodeId >= 0 && nodeId < m_numberNodes); 00080 return m_nodes[nodeId]; 00081 } 00082 00083 void removeNode(int nodeId); 00084 }; 00085 00086 /** IDA* like search keeping a list of the fringe nodes. 00087 This avoids re-expansion of interior nodes at each new 00088 iteration. 00089 00090 %Statistics (see Search::createStatistics and Search::getStatistics): 00091 - "cpu_time" 00092 - "iterations" 00093 - "nodes_expanded" 00094 - "nodes_visited" 00095 - "path_cost" 00096 - "path_length" 00097 */ 00098 template<class MARKER = MarkerFastClear> 00099 class FringeSearch 00100 : public Search 00101 { 00102 public: 00103 FringeSearch(); 00104 00105 StatisticsCollection createStatistics(); 00106 00107 bool findPath(const Environment& env, int start, int target); 00108 00109 const vector<int>& getPath() const 00110 { 00111 return m_path; 00112 } 00113 00114 const StatisticsCollection& getStatistics() const; 00115 00116 const vector<char>& getVisitedNodes() const 00117 { 00118 return m_visitedNodes; 00119 } 00120 00121 private: 00122 /** Cache element for g-value used in FringeSearch. */ 00123 class CacheElement 00124 { 00125 public: 00126 int m_gValue; 00127 00128 int m_iteration; 00129 00130 int m_parent; 00131 }; 00132 00133 00134 int m_nodesExpanded; 00135 00136 int m_nodesVisited; 00137 00138 int m_target; 00139 00140 const Environment* m_env; 00141 00142 Fringe m_fringe; 00143 00144 StatisticsCollection m_statistics; 00145 00146 vector<CacheElement> m_cache; 00147 00148 MARKER m_isCacheValid; 00149 00150 vector<char> m_visitedNodes; 00151 00152 vector<int> m_path; 00153 00154 00155 void addCache(int nodeId, int gValue, int iteration, int parent); 00156 00157 bool checkCache(int nodeId, int gValue, int iteration) const; 00158 00159 void constructPath(int start); 00160 00161 void getFromCache(int nodeId, int& gValue, int& parent) const; 00162 00163 void doIteration(int iteration, int fLimit, int& fMin); 00164 00165 void init(int numberNodes); 00166 00167 void markVisitedNode(int nodeId, int iteration); 00168 }; 00169 00170 } 00171 00172 //----------------------------------------------------------------------------- 00173 00174 namespace PathFind 00175 { 00176 using namespace std; 00177 00178 template<class MARKER> 00179 FringeSearch<MARKER>::FringeSearch() 00180 : m_statistics(createStatistics()) 00181 { 00182 } 00183 00184 template<class MARKER> 00185 void FringeSearch<MARKER>::addCache(int nodeId, int gValue, int iteration, 00186 int parent) 00187 { 00188 CacheElement& cacheElement = m_cache[nodeId]; 00189 cacheElement.m_gValue = gValue; 00190 cacheElement.m_iteration = iteration; 00191 cacheElement.m_parent = parent; 00192 m_isCacheValid.set(nodeId); 00193 } 00194 00195 template<class MARKER> 00196 bool FringeSearch<MARKER>::checkCache(int nodeId, int gValue, 00197 int iteration) const 00198 { 00199 if (! m_isCacheValid.get(nodeId)) 00200 return false; 00201 const CacheElement& cacheElement = m_cache[nodeId]; 00202 int gValueCached = cacheElement.m_gValue; 00203 return (gValue > gValueCached 00204 || (gValue == gValueCached 00205 && iteration == cacheElement.m_iteration)); 00206 } 00207 00208 template<class MARKER> 00209 void FringeSearch<MARKER>::constructPath(int start) 00210 { 00211 m_path.clear(); 00212 if (! m_isCacheValid.get(m_target)) 00213 return; 00214 int nodeId = m_target; 00215 do 00216 { 00217 m_path.push_back(nodeId); 00218 nodeId = m_cache[nodeId].m_parent; 00219 assert(m_isCacheValid.get(nodeId)); 00220 } 00221 while (nodeId != start); 00222 m_path.push_back(nodeId); 00223 } 00224 00225 template<class MARKER> 00226 StatisticsCollection FringeSearch<MARKER>::createStatistics() 00227 { 00228 StatisticsCollection collection; 00229 collection.create("cpu_time"); 00230 collection.create("iterations"); 00231 collection.create("nodes_expanded"); 00232 collection.create("nodes_visited"); 00233 collection.create("path_cost"); 00234 collection.create("path_length"); 00235 return collection; 00236 } 00237 00238 template<class MARKER> 00239 void FringeSearch<MARKER>::doIteration(int iteration, int fLimit, 00240 int& fMin) 00241 { 00242 m_fringe.startIteration(); 00243 vector<Environment::Successor> successors; 00244 while (true) 00245 { 00246 int nodeId = m_fringe.getCurrentNode(); 00247 if (nodeId == NO_NODE) 00248 return; 00249 ++m_nodesVisited; 00250 markVisitedNode(nodeId, iteration); 00251 int gValue; 00252 int lastNode; 00253 getFromCache(nodeId, gValue, lastNode); 00254 if (nodeId == m_target) 00255 { 00256 m_statistics.get("path_cost").add(gValue); 00257 return; 00258 } 00259 int fValue = gValue + m_env->getHeuristic(nodeId, m_target); 00260 if (fValue > fLimit) 00261 { 00262 if (fValue < fMin) 00263 fMin = fValue; 00264 m_fringe.nextNode(); 00265 continue; 00266 } 00267 m_env->getSuccessors(nodeId, lastNode, successors); 00268 int numberSuccessors = successors.size(); 00269 // Reverse loop so that nodes are visited in same order as in IDA* 00270 for (int i = numberSuccessors; i--; ) 00271 { 00272 const Environment::Successor& successor = successors[i]; 00273 int targetNodeId = successor.m_target; 00274 int targetGValue = gValue + successor.m_cost; 00275 if (checkCache(targetNodeId, targetGValue, iteration)) 00276 continue; 00277 m_fringe.insertNode(targetNodeId); 00278 addCache(targetNodeId, targetGValue, iteration, nodeId); 00279 ++m_nodesExpanded; 00280 } 00281 m_fringe.removeCurrentNode(); 00282 m_fringe.nextNode(); 00283 } 00284 } 00285 00286 template<class MARKER> 00287 bool FringeSearch<MARKER>::findPath(const Environment& env, int start, 00288 int target) 00289 { 00290 assert(env.isValidNodeId(start)); 00291 assert(env.isValidNodeId(target)); 00292 clock_t startTime = clock(); 00293 m_env = &env; 00294 m_target = target; 00295 int numberNodes = env.getNumberNodes(); 00296 init(numberNodes); 00297 m_nodesExpanded = 0; 00298 m_nodesVisited = 0; 00299 int iteration = 1; 00300 int fLimit = env.getHeuristic(start, target); 00301 m_fringe.insertNode(start); 00302 addCache(start, 0, 0, NO_NODE); 00303 while (! m_isCacheValid.get(m_target)) 00304 { 00305 //cerr << "limit = " << fLimit << '\n'; 00306 int fMin = INT_MAX; 00307 doIteration(iteration, fLimit, fMin); 00308 fLimit = fMin; 00309 ++iteration; 00310 //m_fringe.print(cerr); 00311 } 00312 constructPath(start); 00313 m_statistics.get("iterations").add(iteration); 00314 double timeDiff = 00315 static_cast<double>(clock() - startTime) / CLOCKS_PER_SEC; 00316 m_statistics.get("nodes_visited").add(m_nodesVisited); 00317 m_statistics.get("nodes_expanded").add(m_nodesExpanded); 00318 m_statistics.get("path_length").add(m_path.size()); 00319 m_statistics.get("cpu_time").add(timeDiff); 00320 return true; 00321 } 00322 00323 template<class MARKER> 00324 void FringeSearch<MARKER>::getFromCache(int nodeId, int& gValue, 00325 int& parent) const 00326 { 00327 assert(m_isCacheValid.get(nodeId)); 00328 const CacheElement& cacheElement = m_cache[nodeId]; 00329 parent = cacheElement.m_parent; 00330 gValue = cacheElement.m_gValue; 00331 } 00332 00333 template<class MARKER> 00334 const StatisticsCollection& FringeSearch<MARKER>::getStatistics() const 00335 { 00336 return m_statistics; 00337 } 00338 00339 template<class MARKER> 00340 void FringeSearch<MARKER>::init(int numberNodes) 00341 { 00342 m_visitedNodes.resize(numberNodes); 00343 fill(m_visitedNodes.begin(), m_visitedNodes.end(), ' '); 00344 m_isCacheValid.init(numberNodes); 00345 m_cache.resize(numberNodes); 00346 m_fringe.init(numberNodes); 00347 m_statistics.clear(); 00348 } 00349 00350 template<class MARKER> 00351 void FringeSearch<MARKER>::markVisitedNode(int nodeId, int iteration) 00352 { 00353 if (m_visitedNodes[nodeId] != ' ') 00354 return; 00355 m_visitedNodes[nodeId] = getVisitedNodeLabel(iteration); 00356 } 00357 00358 } 00359 00360 //----------------------------------------------------------------------------- 00361 #endif