EasyLocalpp  3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
neighborhoodexplorer.hh
Go to the documentation of this file.
1 #if !defined(_NEIGHBORHOOD_EXPLORER_HH_)
2 #define _NEIGHBORHOOD_EXPLORER_HH_
3 
4 #include <typeinfo>
5 #include <iostream>
6 #include <stdexcept>
7 #include <memory>
8 #include <iterator>
9 #include <functional>
10 
14 
15 namespace EasyLocal {
16 
17  namespace Core {
18 
20  class EmptyNeighborhood : public std::logic_error
21  {
22  public:
23  EmptyNeighborhood() : std::logic_error("Empty neighborhood") {}
24  };
25 
26  template <class Move, typename CFtype>
28  {
30  EvaluatedMove() : is_valid(false) {}
31  EvaluatedMove(const Move& move) : move(move), is_valid(false) {}
32  EvaluatedMove(const Move& move, CostStructure<CFtype> cost) : is_valid(true), move(move), cost(cost) {}
33 
34  Move move;
35  bool is_valid;
37  };
38 
39  template <class Move, typename CFtype>
40  std::ostream& operator<<(std::ostream& os, const EvaluatedMove<Move, CFtype>& em)
41  {
42  os << em.move;
43  if (em.is_valid)
44  os << " " << em.cost;
45  else
46  os << " not_valid";
47  return os;
48  }
49 
50  template <class Move, typename CFtype>
51  EvaluatedMove<Move, CFtype> EvaluatedMove<Move, CFtype>::empty = EvaluatedMove<Move, CFtype>();
52 
57  template <class Input, class State, class Move, typename CFtype = int>
58  class NeighborhoodExplorer
59  {
60  public:
61  typedef Input InputType;
62  typedef Move MoveType;
63  typedef State StateType;
64  typedef CFtype CostType;
65 
66  typedef typename std::function<bool(const Move& mv, CostStructure<CFtype> move_cost)> MoveAcceptor;
67 
68  /* Copies all the delta cost components from another neighborhood explorer of the same class
69  @param ne the neighborhood explorer from which the data has to be copied
70  */
72  {
73  this->delta_hard_cost_components = ne.delta_hard_cost_components;
74  this->delta_soft_cost_components = ne.delta_soft_cost_components;
75  this->dcc_adapters = ne.dcc_adapters;
76  this->unimplemented_hard_components = ne.unimplemented_hard_components;
77  this->unimplemented_soft_components = ne.unimplemented_soft_components;
78  }
79 
85  virtual bool FeasibleMove(const State &st, const Move& mv) const
86  { return true; }
87 
93  virtual void RandomMove(const State &st, Move& mv) const throw (EmptyNeighborhood) = 0;
94 
100  virtual void FirstMove(const State& st, Move& mv) const throw (EmptyNeighborhood) = 0;
101 
108  virtual bool NextMove(const State &st, Move& mv) const = 0;
109 
115  virtual void MakeMove(State &st, const Move& mv) const = 0;
116 
122  virtual CostStructure<CFtype> DeltaCostFunctionComponents(const State& st, const Move& mv, const std::vector<double>& weights = std::vector<double>(0)) const;
123 
128 
135 
139  virtual size_t DeltaCostComponents() const
140  {
142  }
143 
147  virtual size_t Modality() const
148  { return 1; }
149 
158 
160 
165  virtual EvaluatedMove<Move, CFtype> SelectFirst(const State& st, size_t& explored, const MoveAcceptor& AcceptMove, const std::vector<double>& weights = std::vector<double>(0)) const throw (EmptyNeighborhood);
166 
171  virtual EvaluatedMove<Move, CFtype> SelectBest(const State& st, size_t& explored, const MoveAcceptor& AcceptMove, const std::vector<double>& weights = std::vector<double>(0)) const throw (EmptyNeighborhood);
172 
177  virtual EvaluatedMove<Move, CFtype> RandomFirst(const State& st, size_t samples, size_t& explored, const MoveAcceptor& AcceptMove, const std::vector<double>& weights = std::vector<double>(0)) const throw (EmptyNeighborhood);
178 
183  virtual EvaluatedMove<Move, CFtype> RandomBest(const State& st, size_t samples, size_t& explored, const MoveAcceptor& AcceptMove, const std::vector<double>& weights = std::vector<double>(0)) const throw (EmptyNeighborhood);
184 
185  protected:
186 
187  const Input& in;
188  StateManager<Input, State, CFtype>& sm;
192 
194  std::vector<std::shared_ptr<DeltaCostComponentAdapter<Input, State, Move, CFtype>>> dcc_adapters;
195 
197  std::string name;
198 
201  };
202 
205  template <class Input, class State, class Move, typename CFtype>
206  NeighborhoodExplorer<Input, State, Move, CFtype>::NeighborhoodExplorer(const Input& i, StateManager<Input, State, CFtype>& e_sm, std::string e_name)
207  : in(i), sm(e_sm), name(e_name), unimplemented_hard_components(false), unimplemented_soft_components(false)
208  {}
209 
215  template <class Input, class State, class Move, typename CFtype>
216  CostStructure<CFtype> NeighborhoodExplorer<Input, State, Move, CFtype>::DeltaCostFunctionComponents(const State& st, const Move & mv, const std::vector<double>& weights) const
217  {
218  CFtype delta_hard_cost = 0, delta_soft_cost = 0;
219  double delta_weighted_cost = 0.0;
220  std::vector<CFtype> delta_cost_function(CostComponent<Input, State, CFtype>::CostComponents(), (CFtype)0);
221 
222  for (size_t i = 0; i < delta_hard_cost_components.size(); i++)
223  {
225  if (dcc->IsDeltaImplemented())
226  {
227  CFtype current_delta_cost = delta_cost_function[dcc->Index()] = dcc->DeltaCost(st, mv);
228  delta_hard_cost += current_delta_cost;
229  if (!weights.empty())
230  delta_weighted_cost += HARD_WEIGHT * weights[dcc->Index()] * current_delta_cost;
231  }
232  }
233  for (size_t i = 0; i < delta_soft_cost_components.size(); i++)
234  {
236  if (dcc->IsDeltaImplemented())
237  {
238  CFtype current_delta_cost = delta_cost_function[dcc->Index()] = dcc->DeltaCost(st, mv);
239  delta_soft_cost += current_delta_cost;
240  if (!weights.empty())
241  delta_weighted_cost += weights[dcc->Index()] * current_delta_cost;
242  }
243  }
244 
245  // only if there is at least one unimplemented delta cost component (i.e., a wrapper along a cost component)
247  {
248  // compute move
249  State new_st = st;
250  MakeMove(new_st, mv);
251 
253  for (size_t i = 0; i < delta_hard_cost_components.size(); i++)
254  {
256  if (!dcc->IsDeltaImplemented())
257  {
258  // get reference to cost component
260  CFtype current_delta_cost = delta_cost_function[cc.Index()] = cc.Weight() * (cc.ComputeCost(new_st) - cc.ComputeCost(st));
261  delta_hard_cost += current_delta_cost;
262  if (!weights.empty())
263  delta_weighted_cost += HARD_WEIGHT * weights[cc.Index()] * current_delta_cost;
264  }
265  }
267  for (size_t i = 0; i < delta_soft_cost_components.size(); i++)
268  {
270  if (!dcc->IsDeltaImplemented())
271  {
272  // get reference to cost component
274  CFtype current_delta_cost = delta_cost_function[cc.Index()] = cc.Weight() * (cc.ComputeCost(new_st) - cc.ComputeCost(st));
275  delta_soft_cost += current_delta_cost;
276  if (!weights.empty())
277  delta_weighted_cost += weights[cc.Index()] * current_delta_cost;
278  }
279  }
280  }
281 
282  if (!weights.empty())
283  return CostStructure<CFtype>(HARD_WEIGHT * delta_hard_cost + delta_soft_cost, delta_weighted_cost, delta_hard_cost, delta_soft_cost, delta_cost_function);
284  else
285  return CostStructure<CFtype>(HARD_WEIGHT * delta_hard_cost + delta_soft_cost, delta_hard_cost, delta_soft_cost, delta_cost_function);
286  }
287 
288  template <class Input, class State, class Move, typename CFtype>
290  {
291  if (dcc.IsHard())
292  delta_hard_cost_components.push_back(&dcc);
293  else
294  delta_soft_cost_components.push_back(&dcc);
295  }
296 
297  template <class Input, class State, class Move, typename CFtype>
299  {
300 
301  dcc_adapters.push_back(std::make_shared<DeltaCostComponentAdapter<Input, State, Move, CFtype>>(in, cc, *this));
302  if (cc.IsHard())
303  {
305  delta_hard_cost_components.push_back(dcc_adapters[dcc_adapters.size() - 1].get());
306  }
307  else
308  {
310  delta_soft_cost_components.push_back(dcc_adapters[dcc_adapters.size() - 1].get());
311  }
312  }
313 
318  template <class Input, class State, class Move, typename CFtype>
319  EvaluatedMove<Move, CFtype> NeighborhoodExplorer<Input, State, Move, CFtype>::SelectFirst(const State& st, size_t& explored, const MoveAcceptor& AcceptMove, const std::vector<double>& weights) const throw (EmptyNeighborhood)
320  {
321  explored = 0;
323  FirstMove(st, mv.move);
324  do
325  {
326  mv.cost = DeltaCostFunctionComponents(st, mv.move, weights);
327  explored++;
328  mv.is_valid = true;
329 
330  if (AcceptMove(mv.move, mv.cost))
331  return mv; // mv passes the acceptance criterion
332  }
333  while (NextMove(st, mv.move));
334 
335  // exiting this loop means that there is no mv passing the acceptance criterion
337  }
338 
343  template <class Input, class State, class Move, typename CFtype>
344  EvaluatedMove<Move, CFtype> NeighborhoodExplorer<Input, State, Move, CFtype>::SelectBest(const State& st, size_t& explored, const MoveAcceptor& AcceptMove, const std::vector<double>& weights) const throw (EmptyNeighborhood)
345  {
346  unsigned int number_of_bests = 0; // number of moves found with the same best value
347  explored = 0;
348  EvaluatedMove<Move, CFtype> mv, best_move;
349  FirstMove(st, mv.move);
350 
351  do
352  {
353  mv.cost = DeltaCostFunctionComponents(st, mv.move, weights);
354  explored++;
355  mv.is_valid = true;
356  if (AcceptMove(mv.move, mv.cost))
357  {
358  if (number_of_bests == 0)
359  {
360  best_move = mv;
361  number_of_bests = 1;
362  }
363  else if (mv.cost < best_move.cost)
364  {
365  best_move = mv;
366  number_of_bests = 1;
367  }
368  else if (mv.cost == best_move.cost)
369  {
370  if (Random::Int(0, number_of_bests) == 0) // accept the move with probability 1 / (1 + number_of_bests)
371  best_move = mv;
372  number_of_bests++;
373  }
374  }
375  }
376  while (NextMove(st, mv.move));
377 
378  if (number_of_bests == 0)
380 
381  return best_move;
382  }
383 
388  template <class Input, class State, class Move, typename CFtype>
389  EvaluatedMove<Move, CFtype> NeighborhoodExplorer<Input, State, Move, CFtype>::RandomFirst(const State& st, size_t samples, size_t& explored, const MoveAcceptor& AcceptMove, const std::vector<double>& weights) const throw (EmptyNeighborhood)
390  {
392  explored = 0;
393  while (explored < samples)
394  {
395  RandomMove(st, mv.move);
396  mv.cost = DeltaCostFunctionComponents(st, mv.move, weights);
397  mv.is_valid = true;
398  explored++;
399  if (AcceptMove(mv.move, mv.cost))
400  return mv;
401  }
402  // exiting this loop means that there is no mv passing the acceptance criterion
404  }
405 
410  template <class Input, class State, class Move, typename CFtype>
411  EvaluatedMove<Move, CFtype> NeighborhoodExplorer<Input, State, Move, CFtype>::RandomBest(const State& st, size_t samples, size_t& explored, const MoveAcceptor& AcceptMove, const std::vector<double>& weights) const throw (EmptyNeighborhood)
412  {
413  unsigned int number_of_bests = 0; // number of moves found with the same best value
414  EvaluatedMove<Move, CFtype> mv, best_move;
415 
416  explored = 0;
417  while (explored < samples)
418  {
419  RandomMove(st, mv.move);
420  mv.cost = DeltaCostFunctionComponents(st, mv.move, weights);
421  mv.is_valid = true;
422  explored++;
423  if (AcceptMove(mv.move, mv.cost))
424  {
425  if (number_of_bests == 0)
426  {
427  best_move = mv;
428  number_of_bests = 1;
429  }
430  else if (mv.cost < best_move.cost)
431  {
432  best_move = mv;
433  number_of_bests = 1;
434  }
435  else if (mv.cost == best_move.cost)
436  {
437  if (Random::Int(0, number_of_bests) == 0) // accept the move with probability 1 / (1 + number_of_bests)
438  best_move = mv;
439  number_of_bests++;
440  }
441  }
442  }
443 
444  if (number_of_bests == 0)
446 
447  return best_move;
448  }
449  }
450 }
451 
452 #endif // _NEIGHBORHOOD_EXPLORER_HH_
virtual bool FeasibleMove(const State &st, const Move &mv) const
virtual void AddDeltaCostComponent(DeltaCostComponent< Input, State, Move, CFtype > &dcc)
std::function< bool(const Move &mv, CostStructure< CFtype > move_cost)> MoveAcceptor
virtual CostStructure< CFtype > DeltaCostFunctionComponents(const State &st, const Move &mv, const std::vector< double > &weights=std::vector< double >(0)) const
EvaluatedMove(const Move &move, CostStructure< CFtype > cost)
virtual CFtype DeltaCost(const State &st, const Move &mv) const
std::vector< DeltaCostComponent< Input, State, Move, CFtype > * > delta_soft_cost_components
void CopyDeltaCostComponents(const NeighborhoodExplorer< Input, State, Move, CFtype > &ne)
virtual EvaluatedMove< Move, CFtype > RandomFirst(const State &st, size_t samples, size_t &explored, const MoveAcceptor &AcceptMove, const std::vector< double > &weights=std::vector< double >(0)) const
CostComponent< Input, State, CFtype > & GetCostComponent() const
const int HARD_WEIGHT
NeighborhoodExplorer(const Input &in, StateManager< Input, State, CFtype > &sm, std::string name)
virtual CFtype ComputeCost(const State &st) const =0
std::vector< DeltaCostComponent< Input, State, Move, CFtype > * > delta_hard_cost_components
std::vector< std::shared_ptr< DeltaCostComponentAdapter< Input, State, Move, CFtype > > > dcc_adapters
virtual bool NextMove(const State &st, Move &mv) const =0
virtual void MakeMove(State &st, const Move &mv) const =0
virtual EvaluatedMove< Move, CFtype > SelectFirst(const State &st, size_t &explored, const MoveAcceptor &AcceptMove, const std::vector< double > &weights=std::vector< double >(0)) const
The class CostComponent manages one single component of the cost, either hard or soft.
virtual void RandomMove(const State &st, Move &mv) const =0
StateManager< Input, State, CFtype > & sm
virtual EvaluatedMove< Move, CFtype > RandomBest(const State &st, size_t samples, size_t &explored, const MoveAcceptor &AcceptMove, const std::vector< double > &weights=std::vector< double >(0)) const
virtual void AddCostComponent(CostComponent< Input, State, CFtype > &cc)
This component is responsible for all operations on the state which are independent of the neighborho...
static int Int()
Definition: random.hh:33
virtual EvaluatedMove< Move, CFtype > SelectBest(const State &st, size_t &explored, const MoveAcceptor &AcceptMove, const std::vector< double > &weights=std::vector< double >(0)) const
virtual void FirstMove(const State &st, Move &mv) const =0