EasyLocalpp  3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
movetester.hh
Go to the documentation of this file.
1 #if !defined(_MOVE_TESTER_HH_)
2 #define _MOVE_TESTER_HH_
3 
4 #include <map>
5 #include <iterator>
6 #include <cmath>
7 #include <chrono>
8 
13 
14 namespace EasyLocal {
15 
16  namespace Debug {
17 
22  template <class Input, class Output, class State, class Move, typename CFtype = int>
23  class MoveTester
24  : public ComponentTester<Input, Output, State, CFtype>, public ChoiceReader<Input, State, CFtype>
25  {
26  public:
27  MoveTester(const Input& in,
31  std::string name,
33  std::ostream& o = std::cout);
34  void RunMainMenu(State& st);
35  void PrintNeighborhoodStatistics(const State& st) const;
36  void PrintAllNeighbors(const State& st) const;
37  void CheckNeighborhoodCosts(const State& st) const;
38  void PrintMoveCosts(const State& st, const EvaluatedMove<Move, CFtype>& em) const;
39  void CheckMoveIndependence(const State& st) const;
40  void CheckRandomMoveDistribution(const State& st) const;
41  size_t Modality() const;
42  void SetTolerance(double t);
43  protected:
44  void ShowMenu();
45  bool ExecuteChoice(State& st);
46  const Input& in;
47  Output out;
51  int choice;
52  std::ostream& os;
53  double tolerance;
54  };
55 
56  /*************************************************************************
57  * Implementation
58  *************************************************************************/
59 
60  template <class Input, class Output, class State, class Move, typename CFtype>
65  std::string name,
67  std::ostream& o)
68  : ComponentTester<Input, Output, State, CFtype>(name), in(i), out(i), sm(sm), om(om), ne(ne), os(o), tolerance(std::numeric_limits<CFtype>::epsilon())
69  {
70  t.AddMoveTester(*this);
71  }
72 
73 
74  template <class Input, class Output, class State, class Move, typename CFtype>
76  {
77  bool show_state;
78  do
79  {
80  ShowMenu();
81  if (choice != 0)
82  {
83  std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
84  show_state = ExecuteChoice(st);
85  std::chrono::milliseconds duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
86  if (show_state)
87  {
88  om.OutputState(st, out);
89  os << "CURRENT SOLUTION " << std::endl << out << std::endl;
90  os << "CURRENT COST : " << sm.CostFunctionComponents(st) << std::endl;
91  }
92  os << "ELAPSED TIME : " << duration.count() / 1000.0 << 's' << std::endl;
93  }
94  }
95  while (choice != 0);
96  os << "Leaving " << this->name << " menu" << std::endl;
97  }
98 
102  template <class Input, class Output, class State, class Move, typename CFtype>
104  {
105  os << "Move Menu: " << std::endl
106  << " (1) Perform Best Move" << std::endl
107  << " (2) Perform First Improving Move" << std::endl
108  << " (3) Perform Random Move" << std::endl
109  << " (4) Perform Input Move" << std::endl
110  << " (5) Print All Neighbors" << std::endl
111  << " (6) Print Neighborhood Statistics" << std::endl
112  << " (7) Print Random Move Cost" << std::endl
113  << " (8) Print Input Move Cost" << std::endl
114  << " (9) Check Neighborhood Costs" << std::endl
115  << " (10) Check Move Independence" << std::endl
116  << " (11) Check Random Move Distribution" << std::endl;
117  os << " (0) Return to Main Menu" << std::endl
118  << " Your choice: ";
119  choice = this->ReadChoice(std::cin);
120  }
121 
127  template <class Input, class Output, class State, class Move, typename CFtype>
129  {
131  try
132  {
133  size_t explored;
134  switch(choice)
135  {
136  case 1:
137  em = ne.SelectBest(st, explored, [](const Move& mv, CostStructure<CFtype> cost) { return true; });
138  break;
139  case 2:
140  em = ne.SelectFirst(st, explored, [](const Move& mv, CostStructure<CFtype> cost) { return cost.total < 0; });
141  break;
142  case 3:
143  em = ne.RandomFirst(st, 1, explored, [](const Move& mv, CostStructure<CFtype> cost) { return true; });
144  break;
145  case 4:
146  os << "Input move : ";
147  std::cin >> em.move;
148  break;
149  case 5:
150  PrintAllNeighbors(st);
151  break;
152  case 6:
153  PrintNeighborhoodStatistics(st);
154  break;
155  case 7:
156  em = ne.RandomFirst(st, 1, explored, [](const Move& mv, CostStructure<CFtype> cost) { return true; });
157  PrintMoveCosts(st, em);
158  break;
159  case 8:
160  os << "Input move : ";
161  std::cin >> em.move;
162  em.cost = ne.DeltaCostFunctionComponents(st, em.move);
163  PrintMoveCosts(st, em);
164  break;
165  case 9:
166  CheckNeighborhoodCosts(st);
167  break;
168  case 10:
169  CheckMoveIndependence(st);
170  break;
171  case 11:
172  CheckRandomMoveDistribution(st);
173  break;
174  default:
175  os << "Invalid choice" << std::endl;
176  }
177  if (choice == 1 || choice == 2 || choice == 3 || choice == 4)
178  {
179  os << "Move : " << em.move << std::endl;
180  if (!ne.FeasibleMove(st, em.move))
181  os << "Move not feasible" << std::endl;
182  else
183  ne.MakeMove(st, em.move);
184  return true;
185  }
186  }
187  catch (EmptyNeighborhood e)
188  {
189  os << "Empty neighborhood" << std::endl;
190  }
191 
192  return false;
193  }
194 
195  template <class Input, class Output, class State, class Move, typename CFtype>
197  {
198  // it's a tester, so we can avoid optimizations
199  os << "Move : " << em.move << std::endl;
200 
201  // process all delta cost components
202  for (size_t i = 0; i < CostComponent<Input, State, CFtype>::CostComponents(); i++)
203  {
205  os << " " << i << ". " << cc.name << " : " << em.cost;
206 
207  // print * or not, add up to right variable
208  if (cc.IsHard())
209  os << "*";
210  os << std::endl;
211  }
212 
213  os << "Total Delta Violations : " << em.cost.violations << std::endl;
214  os << "Total Delta Objective : " << em.cost.objective << std::endl;
215  os << "Total Delta Cost : " << em.cost.total << std::endl;
216  }
217 
218  template <class Input, class Output, class State, class Move, typename CFtype>
220  {
222  unsigned int move_count = 0;
223  CostStructure<CFtype> error;
224  CostStructure<CFtype> st_cost = this->sm.CostFunctionComponents(st), st1_cost;
225  State st1 = st;
226  bool error_found = false, not_last_move = true;
227  ne.FirstMove(st, em.move);
228  do
229  {
230  move_count++;
231 
232  ne.MakeMove(st1, em.move);
233  em.cost = ne.DeltaCostFunctionComponents(st, em.move);
234  st1_cost = this->sm.CostFunctionComponents(st1);
235  error = st1_cost - em.cost - st_cost;
236  for (size_t i = 0; i < CostComponent<Input, State, CFtype>::CostComponents(); i++)
237  {
238  if (!IsZero(error.all_components[i]) && std::abs(error.all_components[i]) > tolerance)
239  {
240  error_found = true;
241  os << em.move << " " << i << ". " << CostComponent<Input, State, CFtype>::Component(i).name << ": " << st_cost.all_components[i] << std::showpos << em.cost.all_components[i] << std::noshowpos << "!="
242  << st1_cost.all_components[i] << " (error = " << std::showpos << error.all_components[i] << ")" << std::noshowpos << std::endl;
243  os << "Press enter to continue " << std::endl;
244  std::cin.get();
245  }
246  }
247 
248  if (move_count % 100 == 0)
249  std::cerr << '.'; // print dots to show that it is alive
250  not_last_move = ne.NextMove(st, em.move);
251  st1 = st;
252 
253  }
254  while (not_last_move);
255 
256  if (!error_found)
257  os << std::endl << "No error found (for " << move_count << " moves)!" << std::endl;
258  }
259 
267  template <class Input, class Output, class State, class Move, typename CFtype>
269  {
270  unsigned int neighbors = 0, improving_neighbors = 0,
271  worsening_neighbors = 0, non_improving_neighbors = 0;
273  double total_positive_cost = 0.0;
274 
275 
276  std::vector<std::pair<CFtype, CFtype>> min_max_costs(CostComponent<Input, State, CFtype>::CostComponents());
277 
278  ne.FirstMove(st, em.move);
279 
280  do
281  {
282  neighbors++;
283  em.cost = ne.DeltaCostFunctionComponents(st, em.move);
284 
285  if (em.cost.total < 0)
286  improving_neighbors++;
287  else if (em.cost.total > 0)
288  {
289  worsening_neighbors++;
290  total_positive_cost += em.cost.total;
291  }
292  else
293  non_improving_neighbors++;
294  for (size_t i = 0; i < CostComponent<Input, State, CFtype>::CostComponents(); i++)
295  {
296  if (em.cost.all_components[i] < min_max_costs[i].first)
297  min_max_costs[i].first = em.cost.all_components[i];
298  else if (em.cost.all_components[i] > min_max_costs[i].second)
299  min_max_costs[i].second = em.cost.all_components[i];
300  }
301  }
302  while (ne.NextMove(st, em.move));
303 
304  os << "Neighborhood size: " << neighbors << std::endl
305  << " improving moves: " << improving_neighbors << " ("
306  << (100.0 * improving_neighbors) / neighbors << "%)" << std::endl
307  << " worsening moves: " << worsening_neighbors << " ("
308  << (100.0 * worsening_neighbors) / neighbors << "%), average cost: " << total_positive_cost / neighbors << std::endl
309  << " sideways moves: " << non_improving_neighbors << " ("
310  << (100.0 * non_improving_neighbors) /neighbors << "%)" << std::endl;
311 
312  os << "Min and max component costs:" << std::endl;
313  for (size_t i = 0; i < CostComponent<Input, State, CFtype>::CostComponents(); i++)
314  os << " " << i << ". " << CostComponent<Input, State, CFtype>::Component(i).name << " : Min = " << min_max_costs[i].first << ", Max = " << min_max_costs[i].second << std::endl;
315  }
316 
317  template <class Input, class Output, class State, class Move, typename CFtype>
319  {
320  Move mv;
321  ne.FirstMove(st, mv);
322  do
323  {
324  os << mv << " " << ne.DeltaCostFunctionComponents(st, mv) << std::endl;
325  }
326  while (ne.NextMove(st, mv));
327  }
328 
329  template <class Input, class Output, class State, class Move, typename CFtype>
331  {
332  Move mv;
333  std::map<Move, unsigned int> frequency;
334  typename std::map<Move, unsigned int>:: iterator it;
335 
336  unsigned long int trials = 0, tot_trials, rounds;
337  double dev = 0;
338 
339  ne.FirstMove(st, mv);
340  do
341  {
342  frequency[mv] = 0;
343  }
344  while (ne.NextMove(st, mv));
345 
346  os << "The neighborhood has " << frequency.size() << " members." << std::endl;
347  os << "How many rounds do you want to test: ";
348  std::cin >> rounds;
349 
350  tot_trials = frequency.size() * rounds;
351  while (trials < tot_trials)
352  {
353  ne.RandomMove(st, mv);
354  if (frequency.find(mv) != frequency.end())
355  {
356  frequency[mv]++;
357  }
358  else
359  os << "Random move not in neighborhood " << mv << std::endl;
360  trials++;
361  if (trials % frequency.size() == 0)
362  std::cerr << '.';
363  }
364 
365  // Compute the standard deviation
366  for (it = frequency.begin(); it != frequency.end(); ++it)
367  {
368  dev += pow((double)(*it).second, 2);
369  }
370 
371  dev = sqrt(fabs(dev/frequency.size() - pow(double(rounds), 2)));
372 
373  double error = 0;
374 
375  os << "Outlier moves [move frequency]:" << std::endl;
376  for (it = frequency.begin(); it != frequency.end(); ++it)
377  {
378  if (fabs((*it).second - double(rounds)) > 3*dev || (*it).second == 0)
379  {
380  error++;
381  os << it->first << " " << it->second/double(rounds) << std::endl;
382  }
383  }
384  std::cerr << "Deviation of move frequency: " << dev << std::endl;
385  std::cerr << "Percentage of outliers " << 100 * error/frequency.size() << '%' << std::endl;
386  }
387 
388  template <class Input, class Output, class State, class Move, typename CFtype>
390  {
391  Move mv;
392  std::vector<std::pair<Move, State> > reached_states;
393  unsigned int repeat_states = 0, null_moves = 0, all_moves = 1, i;
394  bool repeated_state;
395  State st1 = st;
396  ne.FirstMove(st1, mv);
397  ne.MakeMove(st1, mv);
398  if (st1 == st)
399  {
400  os << "Null move " << mv << std::endl;
401  null_moves++;
402  }
403  else
404  {
405  reached_states.push_back(std::make_pair(mv, st1));
406  }
407  while (ne.NextMove(st, mv))
408  {
409  st1 = st;
410  ne.MakeMove(st1, mv);
411  if (st1 == st)
412  {
413  os << "Null move " << mv << std::endl;
414  null_moves++;
415  }
416  else
417  {
418  repeated_state = false;
419  for (i = 0; i < reached_states.size(); i++)
420  if (st1 == reached_states[i].second)
421  {
422  repeated_state = true;
423  break;
424  }
425  if (repeated_state)
426  {
427  os << "Repeated state for moves " << reached_states[i].first << " and " << mv << std::endl;
428  repeat_states++;
429  }
430  else
431  {
432  reached_states.push_back(std::make_pair(mv, st1));
433  }
434  }
435  if (all_moves % 100 == 0)
436  std::cerr << '.'; // print dots to show that it is alive
437  all_moves++;
438  }
439 
440  os << std::endl << "Number of moves: " << all_moves << std::endl;
441  if (repeat_states == 0)
442  os << "No repeated states" << std::endl;
443  else
444  os << "There are " << repeat_states << " repeated states" << std::endl;
445  if (null_moves == 0)
446  os << "No null moves" << std::endl;
447  else
448  os << "There are " << null_moves << " null moves" << std::endl;
449  }
450 
451  template <class Input, class Output, class State, class Move, typename CFtype>
453  {
454  return ne.Modality();
455  }
456 
457  template <class Input, class Output, class State, class Move, typename CFtype>
459  {
460  tolerance = t;
461  }
462 
463  }
464 }
465 
466 #endif // _MOVE_TESTER_HH_
void AddMoveTester(ComponentTester< Input, Output, State, CFtype > &amt)
Definition: tester.hh:149
std::vector< CFtype > all_components
Definition: statemanager.hh:25
Core::NeighborhoodExplorer< Input, State, Move, CFtype > & ne
Definition: movetester.hh:50
Core::OutputManager< Input, Output, State, CFtype > & om
Definition: movetester.hh:49
Core::StateManager< Input, State, CFtype > & sm
Definition: movetester.hh:48
void CheckRandomMoveDistribution(const State &st) const
Definition: movetester.hh:330
void PrintMoveCosts(const State &st, const EvaluatedMove< Move, CFtype > &em) const
Definition: movetester.hh:196
void PrintAllNeighbors(const State &st) const
Definition: movetester.hh:318
void RunMainMenu(State &st)
Definition: movetester.hh:75
void CheckMoveIndependence(const State &st) const
Definition: movetester.hh:389
void CheckNeighborhoodCosts(const State &st) const
Definition: movetester.hh:219
The class CostComponent manages one single component of the cost, either hard or soft.
bool ExecuteChoice(State &st)
Definition: movetester.hh:128
bool IsZero(CFtype value)
MoveTester(const Input &in, Core::StateManager< Input, State, CFtype > &sm, Core::OutputManager< Input, Output, State, CFtype > &om, Core::NeighborhoodExplorer< Input, State, Move, CFtype > &ne, std::string name, Tester< Input, Output, State, CFtype > &t, std::ostream &o=std::cout)
Definition: movetester.hh:61
void PrintNeighborhoodStatistics(const State &st) const
Definition: movetester.hh:268
This component is responsible for all operations on the state which are independent of the neighborho...
static const CostComponent< Input, State, CFtype > & Component(size_t i)