HOG2
ParallelIDAStar.h
Go to the documentation of this file.
1 //
2 // ParallelIDA.h
3 // hog2 glut
4 //
5 // Created by Nathan Sturtevant on 9/2/15.
6 // Copyright (c) 2015 University of Denver. All rights reserved.
7 //
8 
9 #ifndef hog2_glut_ParallelIDA_h
10 #define hog2_glut_ParallelIDA_h
11 
12 #include <iostream>
13 #include "SearchEnvironment.h"
14 #include <unordered_map>
15 #include "FPUtil.h"
16 #include "vectorCache.h"
17 #include "SharedQueue.h"
18 #include <thread>
19 
20 const int workDepth = 5;
21 
22 template <class action>
23 struct workUnit {
24  action pre[workDepth];
25  std::vector<action> solution;
26  std::vector<int> gHistogram;
27  std::vector<int> fHistogram;
28  double nextBound;
29  uint64_t expanded, touched;
31 };
32 
33 template <class environment, class state, class action>
35 public:
37  virtual ~ParallelIDAStar() {}
38  // void GetPath(environment *env, state from, state to,
39  // std::vector<state> &thePath);
40  void GetPath(environment *env, state from, state to,
41  std::vector<action> &thePath);
42 
43  uint64_t GetNodesExpanded() { return nodesExpanded; }
44  uint64_t GetNodesTouched() { return nodesTouched; }
46  void SetHeuristic(Heuristic<state> *heur) { heuristic = heur; if (heur != 0) storedHeuristic = true;}
47 private:
48  unsigned long long nodesExpanded, nodesTouched;
49 
50  void StartThreadedIteration(environment env, state startState, double bound);
51  void DoIteration(environment *env,
52  action forbiddenAction, state &currState,
53  std::vector<action> &thePath, double bound, double g,
55  void GenerateWork(environment *env,
56  action forbiddenAction, state &currState,
57  std::vector<action> &thePath);
58 
60  {
61  return;
62  uint64_t early = 0, late = 0;
63  printf("G-cost distribution\n");
64  for (int x = 0; x < gCostHistogram.size(); x++)
65  {
66  if (gCostHistogram[x] != 0)
67  printf("%d\t%" PRId64 "\n", x, gCostHistogram[x]);
68  if (x*2 > gCostHistogram.size()-1)
69  late += gCostHistogram[x];
70  else
71  early += gCostHistogram[x];
72  }
73  if (late < early)
74  printf("--Strong heuristic - Expect MM > A*\n");
75  else
76  printf("--Weak heuristic - Expect MM >= MM0.\n");
77  printf("\n");
78  printf("F-cost distribution\n");
79  for (int x = 0; x < fCostHistogram.size(); x++)
80  {
81  if (fCostHistogram[x] != 0)
82  printf("%d\t%" PRId64 "\n", x, fCostHistogram[x]);
83  }
84  printf("\n");
85  }
86  void UpdateNextBound(double currBound, double fCost);
87  state goal;
88  double nextBound;
89  //vectorCache<action> actCache;
92  std::vector<uint64_t> gCostHistogram;
93  std::vector<uint64_t> fCostHistogram;
94  std::vector<workUnit<action>> work;
95  std::vector<std::thread*> threads;
98 };
99 
100 //template <class state, class action>
101 //void ParallelIDAStar<environment, state, action>::GetPath(environment *env,
102 // state from, state to,
103 // std::vector<state> &thePath)
104 //{
105 // assert(!"Not Implemented");
106 //}
107 
108 template <class environment, class state, class action>
110  state from, state to,
111  std::vector<action> &thePath)
112 {
113  const auto numThreads = std::thread::hardware_concurrency();
114  if (!storedHeuristic)
115  heuristic = env;
116  nextBound = 0;
117  nodesExpanded = nodesTouched = 0;
118  thePath.resize(0);
119  work.resize(0);
120 
121  // Set class member
122  goal = to;
123 
124  if (env->GoalTest(from, to))
125  return;
126 
127  std::vector<action> act;
128  env->GetActions(from, act);
129 
130  double rootH = heuristic->HCost(from, to);
131  UpdateNextBound(0, rootH);
132 
133  // builds a list of all states at a fixed depth
134  // we will then search them in parallel
135  GenerateWork(env, act[0], from, thePath);
136  for (size_t x = 0; x < work.size(); x++)
137  work[x].unitNumber = x;
138  printf("%lu pieces of work generated\n", work.size());
139  foundSolution = work.size() + 1;
140 
141  while (foundSolution > work.size())
142  {
143  gCostHistogram.clear();
144  gCostHistogram.resize(nextBound+1);
145  fCostHistogram.clear();
146  fCostHistogram.resize(nextBound+1);
147  threads.resize(0);
148 
149  printf("Starting iteration with bound %f; %" PRId64 " expanded, %" PRId64 " generated\n", nextBound, nodesExpanded, nodesTouched);
150  fflush(stdout);
151 
152  for (size_t x = 0; x < work.size(); x++)
153  {
154  q.Add(x);
155  }
156  for (size_t x = 0; x < numThreads; x++)
157  {
158  threads.push_back(new std::thread(&ParallelIDAStar<environment, state, action>::StartThreadedIteration, this,
159  *env, from, nextBound));
160  }
161  for (int x = 0; x < threads.size(); x++)
162  {
163  threads[x]->join();
164  delete threads[x];
165  threads[x] = 0;
166  }
167  double bestBound = (nextBound+1)*10; // FIXME: Better ways to do bounds
168  for (int x = 0; x < work.size(); x++)
169  {
170  for (int y = 0; y < work[x].gHistogram.size(); y++)
171  {
172  gCostHistogram[y] += work[x].gHistogram[y];
173  fCostHistogram[y] += work[x].fHistogram[y];
174  }
175  if (work[x].nextBound > nextBound && work[x].nextBound < bestBound)
176  {
177  //printf("Got bound of %1.0f from work %d\n", work[x].nextBound, x);
178  bestBound = work[x].nextBound;
179  }
180  nodesExpanded += work[x].expanded;
181  nodesTouched += work[x].touched;
182  if (work[x].solution.size() != 0)
183  {
184 // printf(">>Solution histogram>>\n");
185 // PrintGHistogram();
186 // printf("<<Solution histogram<<\n");
187  thePath = work[x].solution;
188  }
189  }
190  nextBound = bestBound;
191 // printf(">>Full histogram>>\n");
192 // PrintGHistogram();
193 // printf("<<Full histogram<<\n");
194  if (thePath.size() != 0)
195  return;
196  }
197 }
198 
199 template <class environment, class state, class action>
201  action forbiddenAction, state &currState,
202  std::vector<action> &thePath)
203 {
204  if (thePath.size() >= workDepth)
205  {
207  for (int x = 0; x < workDepth; x++)
208  {
209  w.pre[x] = thePath[x];
210  }
211  work.push_back(w);
212  return;
213  }
214 
215  std::vector<action> actions;
216  env->GetActions(currState, actions);
217  nodesTouched += actions.size();
218  nodesExpanded++;
219  int depth = (int)thePath.size();
220 
221  for (unsigned int x = 0; x < actions.size(); x++)
222  {
223  if ((depth != 0) && (actions[x] == forbiddenAction))
224  continue;
225 
226  thePath.push_back(actions[x]);
227 
228  env->ApplyAction(currState, actions[x]);
229 // assert(!env->GoalTest(currState));
230 
231  action a = actions[x];
232  env->InvertAction(a);
233  GenerateWork(env, a, currState, thePath);
234  env->UndoAction(currState, actions[x]);
235  thePath.pop_back();
236  }
237 
238 }
239 
240 template <class environment, class state, class action>
241 void ParallelIDAStar<environment, state, action>::StartThreadedIteration(environment env, state startState, double bound)
242 {
243  vectorCache<action> actCache;
244  std::vector<action> thePath;
245  while (true)
246  {
247  int nextValue;
248  // All values put in before threads start. Once the queue is empty we're done
249  if (q.Remove(nextValue) == false)
250  break;
251 
252  thePath.resize(0);
253  bool passedLimit = false;
254  double g = 0;
255  workUnit<action> localWork = work[nextValue];
256  localWork.solution.resize(0);
257  localWork.gHistogram.clear();
258  localWork.gHistogram.resize(bound+1);
259  localWork.fHistogram.clear();
260  localWork.fHistogram.resize(bound+1);
261  localWork.nextBound = 10*bound;//FIXME: Better ways to do this
262  localWork.expanded = 0;
263  localWork.touched = 0;
264 
265  for (int x = 0; x < workDepth; x++)
266  {
267  g += env.GCost(startState, localWork.pre[x]);
268  env.ApplyAction(startState, localWork.pre[x]);
269  thePath.push_back(localWork.pre[x]);
270 
271  if (!passedLimit && fgreater(g+heuristic->HCost(startState, goal), bound))
272  {
273  localWork.nextBound = g+heuristic->HCost(startState, goal);
274  passedLimit = true;
275  }
276  }
277 
278  action last = localWork.pre[workDepth-1];
279  env.InvertAction(last);
280 
281  if (!passedLimit)
282  {
283  DoIteration(&env, last, startState, thePath, bound, g, localWork, actCache);
284  }
285 
286  for (int x = workDepth-1; x >= 0; x--)
287  {
288  env.UndoAction(startState, localWork.pre[x]);
289  g -= env.GCost(startState, localWork.pre[x]);
290  }
291  work[nextValue] = localWork;
292  }
293 }
294 
295 
296 template <class environment, class state, class action>
298  action forbiddenAction, state &currState,
299  std::vector<action> &thePath, double bound, double g,
301 {
302  double h = heuristic->HCost(currState, goal);//, parentH); // TODO: restore code that uses parent h-cost
303 
304  if (fgreater(g+h, bound))
305  {
306  if (g+h < w.nextBound)
307  w.nextBound = g+h;
308  return;
309  }
310 
311  // must do this after we check the f-cost bound
312  if (env->GoalTest(currState, goal))
313  {
314  w.solution = thePath;
315  foundSolution = std::min(foundSolution,w.unitNumber);
316  return;
317  }
318 
319  std::vector<action> &actions = *cache.getItem();
320  env->GetActions(currState, actions);
321  w.touched += actions.size();
322  w.expanded++;
323  w.gHistogram[g]++;
324  w.fHistogram[g+h]++;
325 
326  for (unsigned int x = 0; x < actions.size(); x++)
327  {
328  if (actions[x] == forbiddenAction)
329  continue;
330 
331  thePath.push_back(actions[x]);
332 
333  double edgeCost = env->GCost(currState, actions[x]);
334  env->ApplyAction(currState, actions[x]);
335  action a = actions[x];
336  env->InvertAction(a);
337  DoIteration(env, a, currState, thePath, bound, g+edgeCost, w, cache);
338  env->UndoAction(currState, actions[x]);
339  thePath.pop_back();
340  if (foundSolution <= w.unitNumber)
341  break;
342  }
343  cache.returnItem(&actions);
344 }
345 
346 
347 template <class environment, class state, class action>
349 {
350  if (!fgreater(nextBound, currBound))
351  {
352  nextBound = fCost;
353  //printf("Updating next bound to %f\n", nextBound);
354  }
355  else if (fgreater(fCost, currBound) && fless(fCost, nextBound))
356  {
357  nextBound = fCost;
358  //printf("Updating next bound to %f\n", nextBound);
359  }
360 }
361 
362 #endif
ParallelIDAStar::GetPath
void GetPath(environment *env, state from, state to, std::vector< action > &thePath)
Definition: ParallelIDAStar.h:109
min
double min(double a, double b)
Definition: FPUtil.h:35
ParallelIDAStar::GetNodesExpanded
uint64_t GetNodesExpanded()
Definition: ParallelIDAStar.h:43
ParallelIDAStar::q
SharedQueue< int > q
Definition: ParallelIDAStar.h:96
Heuristic
Definition: Heuristic.h:30
workUnit::touched
uint64_t touched
Definition: ParallelIDAStar.h:29
ParallelIDAStar::fCostHistogram
std::vector< uint64_t > fCostHistogram
Definition: ParallelIDAStar.h:93
ParallelIDAStar::~ParallelIDAStar
virtual ~ParallelIDAStar()
Definition: ParallelIDAStar.h:37
workUnit::fHistogram
std::vector< int > fHistogram
Definition: ParallelIDAStar.h:27
ParallelIDAStar::heuristic
Heuristic< state > * heuristic
Definition: ParallelIDAStar.h:91
workDepth
const int workDepth
Definition: ParallelIDAStar.h:20
FPUtil.h
ParallelIDAStar::nodesExpanded
unsigned long long nodesExpanded
Definition: ParallelIDAStar.h:48
vectorCache.h
ParallelIDAStar::gCostHistogram
std::vector< uint64_t > gCostHistogram
Definition: ParallelIDAStar.h:92
ParallelIDAStar::GenerateWork
void GenerateWork(environment *env, action forbiddenAction, state &currState, std::vector< action > &thePath)
Definition: ParallelIDAStar.h:200
workUnit::pre
action pre[workDepth]
Definition: ParallelIDAStar.h:24
vectorCache::returnItem
void returnItem(std::vector< storage > *)
Definition: vectorCache.h:62
ParallelIDAStar::ResetNodeCount
void ResetNodeCount()
Definition: ParallelIDAStar.h:45
workUnit::nextBound
double nextBound
Definition: ParallelIDAStar.h:28
ParallelIDAStar::work
std::vector< workUnit< action > > work
Definition: ParallelIDAStar.h:94
ParallelIDAStar::threads
std::vector< std::thread * > threads
Definition: ParallelIDAStar.h:95
fless
bool fless(double a, double b)
Definition: FPUtil.h:28
vectorCache::getItem
std::vector< storage > * getItem()
Definition: vectorCache.h:39
ParallelIDAStar::PrintGHistogram
void PrintGHistogram()
Definition: ParallelIDAStar.h:59
workUnit::gHistogram
std::vector< int > gHistogram
Definition: ParallelIDAStar.h:26
fgreater
bool fgreater(double a, double b)
Definition: FPUtil.h:29
workUnit
Definition: ParallelIDAStar.h:23
workUnit::unitNumber
int unitNumber
Definition: ParallelIDAStar.h:30
vectorCache< action >
ParallelIDAStar::DoIteration
void DoIteration(environment *env, action forbiddenAction, state &currState, std::vector< action > &thePath, double bound, double g, workUnit< action > &w, vectorCache< action > &cache)
Definition: ParallelIDAStar.h:297
ParallelIDAStar::UpdateNextBound
void UpdateNextBound(double currBound, double fCost)
Definition: ParallelIDAStar.h:348
workUnit::solution
std::vector< action > solution
Definition: ParallelIDAStar.h:25
ParallelIDAStar::nextBound
double nextBound
Definition: ParallelIDAStar.h:88
workUnit::expanded
uint64_t expanded
Definition: ParallelIDAStar.h:29
SharedQueue.h
ParallelIDAStar::StartThreadedIteration
void StartThreadedIteration(environment env, state startState, double bound)
Definition: ParallelIDAStar.h:241
ParallelIDAStar::foundSolution
int foundSolution
Definition: ParallelIDAStar.h:97
ParallelIDAStar::SetHeuristic
void SetHeuristic(Heuristic< state > *heur)
Definition: ParallelIDAStar.h:46
ParallelIDAStar::goal
state goal
Definition: ParallelIDAStar.h:87
ParallelIDAStar::storedHeuristic
bool storedHeuristic
Definition: ParallelIDAStar.h:90
ParallelIDAStar::ParallelIDAStar
ParallelIDAStar()
Definition: ParallelIDAStar.h:36
ParallelIDAStar::GetNodesTouched
uint64_t GetNodesTouched()
Definition: ParallelIDAStar.h:44
ParallelIDAStar::nodesTouched
unsigned long long nodesTouched
Definition: ParallelIDAStar.h:48
ParallelIDAStar
Definition: ParallelIDAStar.h:34
SharedQueue< int >
SearchEnvironment.h