vrpRouting  0.3
optimizers/simple.cpp
Go to the documentation of this file.
1 /*PGR-GNU*****************************************************************
2 
3 FILE: simple.cpp
4 
5 Copyright (c) 2017 pgRouting developers
7 
8 Developer:
9 Copyright (c) 2017 Celia Virginia Vergara Castillo
10 
11 ------
12 
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
17 
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22 
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 
27  ********************************************************************PGR-GNU*/
28 
29 #include "optimizers/simple.h"
30 
31 #include <algorithm>
32 #include <limits>
33 #include <set>
34 
35 #include "cpp_common/pgr_assert.h"
36 
37 namespace vrprouting {
38 namespace optimizers {
39 namespace simple {
40 
41 
43  const problem::Solution &old_solution,
44  size_t times,
45  const Initials_code& p_kind) :
46  problem::Solution(old_solution),
47  best_solution(old_solution),
48  m_kind(p_kind) {
49  inter_swap(times);
50  this->m_fleet = best_solution.fleet();
51  msg().log << tau("bestSol before sort by size");
52  sort_by_size();
53  msg().log << tau("bestSol after sort by size");
54  msg().log << tau();
55  }
56 
57 
58 void
59 Optimize::inter_swap(size_t times) {
60  msg().log << tau("before sort by size");
61  sort_by_size();
62  msg().log << tau("before decrease");
64  msg().log << tau("after decrease");
65  sort_by_size();
66  msg().log << tau("after sort by size");
67 
68  size_t i = 0;
69  while (i++ < times) {
70  msg().log << "\n*************************** CYCLE" << i;
71  inter_swap();
72  msg().log << tau("after inter swap");
73  std::rotate(m_fleet.begin(), m_fleet.begin() + 1, m_fleet.end());
74  msg().log << tau("before next cycle");
75  }
76 }
77 
78 /*
79  * ordering the trucks by number of orders it has
80  * - the truck with more orders wants to:
81  * - make less time
82  * - do more orders
83  * The inter_swap objective is to make the trucks with more orders
84  * - less time consuming
85  */
86 bool
88  msg().log
89  << "\n" <<tau("before inter swap");
91  auto swapped_f = false;
92  /*
93  * .. to ... from ....
94  */
95  for (auto &from : m_fleet) {
96  for (auto &to : m_fleet) {
97  if (&from == &to) break;
98 
99 #if 0
100  msg().log
101  << "\n to " << to.id()
102  << "from " << from.id();
103  auto swapped = false;
104 #endif
105  swap_worse(to, from);
106  move_reduce_cost(from, to);
107 #if 0
108  msg().log << "++++++++" << p_swaps;
109 #endif
110  }
111  }
112 
113 #if 0
114  while (!p_swaps.empty()) {
115  swapped_f = swap_order() || swapped_f;
116  }
117 #endif
118 
119  msg().log
120  << "\n" <<tau("after");
122 
123  return swapped_f;
124 }
125 
126 
127 
128 /*
129  * .. to ... from ....
130  */
131 bool
133  /*
134  * working on a copy of the vehicles
135  */
136  auto from_truck = from;
137  auto to_truck = to;
138 
139  auto swapped = false;
140 
141  /*
142  * To avoid invalidation of cycles
143  */
144  auto o_from_orders = from_truck.orders_in_vehicle();
145  auto o_to_orders = to_truck.orders_in_vehicle();
146 
147  pgassert((o_from_orders * o_to_orders).empty());
148 
149  for (auto from_orders = from_truck.orders_in_vehicle();
150  !from_orders.empty();
151  from_orders.pop_front()) {
152  auto from_order = from_truck.orders()[from_orders.front()];
153 
154  pgassert(from_truck.has_order(from_order));
155 
156  if (move_order(from_order, from_truck, to_truck)) {
157  pgassert(!from_truck.has_order(from_order));
158  pgassert(to_truck.has_order(from_order));
159  /*
160  * The order could be moved to "to" truck
161  */
162  to = to_truck;
163  from = from_truck;
164  /*
165  * go to next order
166  */
167  pgassert(!from.has_order(from_order));
168  pgassert(to.has_order(from_order));
169  pgassert(!from_truck.has_order(from_order));
170  pgassert(to_truck.has_order(from_order));
171  pgassert(!(from.has_order(from_order) && to.has_order(from_order)));
172  pgassert(from.has_order(from_order) || to.has_order(from_order));
173  continue;
174  }
175 
176  pgassert(from_truck.has_order(from_order));
177  auto curr_from_duration = from_truck.duration();
178 
179  for (auto to_order_id : o_to_orders) {
180  auto to_order = to.orders()[to_order_id];
181  /*
182  * The orders might have being swapped before
183  */
184  if (!to_truck.has_order(to_order)) continue;
185  // if (!from_truck.has_order(from_order)) break; // TODO delete line
186 
187  pgassert(from_truck.has_order(from_order));
188  pgassert(to_truck.has_order(to_order));
189 
190  auto curr_to_duration = to_truck.duration();
191 
192  /*
193  * delete from_order, and to order from their trucks
194  */
195  pgassert(from_truck.has_order(from_order));
196  from_truck.erase(from_order);
197  pgassert(to_truck.has_order(to_order));
198  to_truck.erase(to_order);
199 
200  /*
201  * insert them in the other truck
202  */
204  from_truck.semiLIFO(to_order);
205  to_truck.semiLIFO(from_order);
206  } else {
207  from_truck.hillClimb(to_order);
208  to_truck.hillClimb(from_order);
209  }
210 
211  pgassert((from_truck.has_order(to_order) && from_truck.is_feasible()) || !from_truck.has_order(to_order));
212  pgassert((to_truck.has_order(from_order) && to_truck.is_feasible()) || !to_truck.has_order(from_order));
213 
214  if (from_truck.has_order(to_order) && to_truck.has_order(from_order)) {
215  auto new_from_duration = from_truck.duration();
216  auto new_to_duration = to_truck.duration();
217 
218  auto estimated_delta =
219  - (curr_from_duration + curr_to_duration)
220  + (new_to_duration + new_from_duration);
221 
222  auto estimated_duration = duration() + estimated_delta;
223 
224  /*
225  * Can swap when:
226  * - or from_truck duration is reduced
227  * - the total fleet duration is reduced
228  * - or the new fleet duration is better than best_solution duration
229  */
230  if (new_from_duration < curr_from_duration ||
231  estimated_delta < 0 ||
232  estimated_duration < best_solution.duration()) {
233  /*
234  * this acctually makes the swapping
235  */
236  to = to_truck;
237  from = from_truck;
238 
239  pgassert(!(from.has_order(from_order) && to.has_order(from_order)));
240  pgassert(from.has_order(from_order) || to.has_order(from_order));
241  pgassert(!(from.has_order(to_order) && to.has_order(to_order)));
242  pgassert(from.has_order(to_order) || to.has_order(to_order));
243 
244  swapped = true;
245  break;
246  }
247  }
248  /*
249  * Can't swap, restore vehicles
250  */
251  to_truck = to;
252  from_truck = from;
253 
254  pgassert(!(from.has_order(from_order) && to.has_order(from_order)));
255  pgassert(from.has_order(from_order) || to.has_order(from_order));
256  pgassert(!(from.has_order(to_order) && to.has_order(to_order)));
257  pgassert(from.has_order(to_order) || to.has_order(to_order));
258  }
259  }
260 
261  return false && swapped;
262 }
263 
264 /*
265  * from_truck: position of the truck where the order is
266  * to truck: truck to put the order
267  */
268 bool
270  const problem::Order from_order,
271  problem::Vehicle_pickDeliver &from_truck,
272  const problem::Order to_order,
273  problem::Vehicle_pickDeliver &to_truck) {
274  if (!from_truck.has_order(from_order)
275  || !to_truck.has_order(to_order)) {
276  return false;
277  }
278 
279  pgassert(from_truck.has_order(from_order));
280  pgassert(to_truck.has_order(to_order));
281 
282  from_truck.erase(from_order);
283  to_truck.erase(to_order);
284 
285  /*
286  * insert them in the other truck
287  */
289  from_truck.semiLIFO(to_order);
290  to_truck.semiLIFO(from_order);
291  } else {
292  from_truck.hillClimb(to_order);
293  to_truck.hillClimb(from_order);
294  }
295 
296  pgassert((from_truck.has_order(to_order) && from_truck.is_feasible()) || !from_truck.has_order(to_order));
297  pgassert((to_truck.has_order(from_order) && to_truck.is_feasible()) || !to_truck.has_order(from_order));
298 
299  if (!from_truck.has_order(to_order) || !to_truck.has_order(from_order)) {
300  /*
301  * swap generates a violation: restore trucks
302  */
303  if (from_truck.has_order(to_order)) from_truck.erase(to_order);
304  if (to_truck.has_order(from_order)) to_truck.erase(from_order);
305 
306  pgassert(!from_truck.has_order(to_order));
307  pgassert(!from_truck.has_order(from_order));
308  pgassert(!to_truck.has_order(from_order));
309  pgassert(!to_truck.has_order(to_order));
310 
311  /*
312  * insert them again in the truck
313  */
315  from_truck.semiLIFO(from_order);
316  to_truck.semiLIFO(to_order);
317  } else {
318  from_truck.hillClimb(from_order);
319  to_truck.hillClimb(to_order);
320  }
321  pgassert((from_truck.has_order(from_order) && from_truck.is_feasible()) || !from_truck.has_order(from_order));
322  pgassert((to_truck.has_order(to_order) && to_truck.is_feasible()) || !to_truck.has_order(to_order));
323 
324  return false;
325  }
326 
327  pgassert(from_truck.has_order(to_order));
328  pgassert(to_truck.has_order(from_order));
329  return true;
330 }
331 
332 void
334  std::sort(m_fleet.begin(), m_fleet.end(), []
336  -> bool {
337  return lhs.orders_in_vehicle().size()
338  > rhs.orders_in_vehicle().size();
339  });
340 }
341 
342 void
345  std::stable_sort(m_fleet.begin(), m_fleet.end(), []
347  -> bool {
348  return lhs.orders_in_vehicle().size()
349  > rhs.orders_in_vehicle().size();
350  });
351 }
352 
353 void
355  std::sort(m_fleet.begin(), m_fleet.end(), []
357  -> bool {
358  return lhs.duration() > rhs.duration();
359  });
360 }
361 
362 void
364  m_fleet.erase(std::remove_if(
365  m_fleet.begin(),
366  m_fleet.end(),
367  [](const problem::Vehicle_pickDeliver &v){
368  return v.orders_in_vehicle().empty();}),
369  m_fleet.end());
370  save_if_best();
371 }
372 
373 /*
374  * from_truck trying to make from_truck's duration smaller
375  * - maybe all orders can be moved
376  * - if that is the case, the from_truck could be removed
377  *
378  * Deleting an order on the from_truck
379  * - number of truck remains the same
380  * - from_truk duration() can not get larger
381  * - the overall duration can get larger
382  *
383  */
384 bool
388  pgassert(&from != &to);
389 
390  auto from_truck = from;
391  auto to_truck = to;
392  /*
393  * don't move to empty truck
394  */
395  if (to_truck.empty()) return false;
396 
397  /*
398  * don't move from a real truck to a phoney truck
399  */
400  if (!from_truck.is_phony() && to_truck.is_phony()) {
401  return false;
402  }
403 
404  auto moved = false;
405 
406  auto from_orders = from_truck.orders_in_vehicle();
407  for (const auto o_id : from_orders) {
408  /*
409  * removing an order decreases the duration
410  */
411  auto order = from_truck.orders()[o_id];
412 
413  auto curr_duration = from_truck.duration() + to_truck.duration();
414  /*
415  * insert it in the "to" truck
416  */
417  pgassert(!to_truck.has_order(order));
419  to_truck.semiLIFO(order) :
420  to_truck.hillClimb(order);
421  pgassert((to_truck.has_order(order) && to_truck.is_feasible()) || !to_truck.has_order(order));
422 
423  if (to_truck.has_order(order)) {
424  from_truck.erase(order);
425 
426  auto new_duration = from_truck.duration() + to_truck.duration();
427 
428  /*
429  * cost is reduced
430  */
431  if (new_duration < curr_duration
432  || from_truck.empty()
433  || new_duration < best_solution.duration()) {
434  moved = true;
435  save_if_best();
436  continue;
437  }
438 
439  /*
440  * cost is not reduced
441  * revert changes
442  */
443  to_truck.erase(order);
445  from_truck.semiLIFO(order) :
446  from_truck.hillClimb(order);
447  }
448  }
449  return moved;
450 }
451 
452 
453 
466 bool
468  problem::Order order,
469  problem::Vehicle_pickDeliver &from_truck,
470  problem::Vehicle_pickDeliver &to_truck) {
471  pgassert(from_truck.has_order(order));
472  pgassert(!to_truck.has_order(order));
473  /*
474  * don't move to empty truck
475  */
476  if (to_truck.empty()) return false;
477 
478  /*
479  * don't move from a real truck to a phoney truck
480  */
481  if (!from_truck.is_phony() && to_truck.is_phony()) return false;
482 
483  /*
484  * Don't move from a vehicle with more orders
485  */
486  if (from_truck.size() > to_truck.size()) return false;
487 
488  /*
489  * insert the order
490  */
492  to_truck.semiLIFO(order) :
493  to_truck.hillClimb(order);
494 
495  if (to_truck.has_order(order)) {
496  from_truck.erase(order);
497 
498  pgassert(!(from_truck.has_order(order) && to_truck.has_order(order)));
499  pgassert(!from_truck.has_order(order));
500  pgassert(to_truck.has_order(order));
501  return true;
502  }
503 
504  pgassert(!(from_truck.has_order(order) && to_truck.has_order(order)));
505  pgassert(from_truck.has_order(order));
506  pgassert(!to_truck.has_order(order));
507  return false;
508 }
509 
510 void
512  std::sort(m_fleet.begin(), m_fleet.end(), []
514  -> bool {
515  return lhs.total_wait_time() > rhs.total_wait_time();
516  });
517 
518  std::stable_sort(m_fleet.begin(), m_fleet.end(), []
520  -> bool {
521  return lhs.orders_size() > rhs.orders_size();
522  });
523 }
524 
525 
526 /*
527  * Optimize decreasing truck
528  *
529  * - Objective: try to remove truck with less duration
530  * - Secundary objective, acts like a shake operation
531  *
532  */
533 void
535  bool decreased(false);
536  for (size_t i = 1; i < m_fleet.size(); ++i) {
537  decreased = decrease_truck(i) || decreased;
538  }
539  if (decreased) {
541  save_if_best();
542  decrease_truck();
543  }
544  save_if_best();
545 }
546 
547 bool
549  auto position = cycle;
550  for (auto orders = m_fleet[position].orders_in_vehicle();
551  !orders.empty();
552  orders.pop_front()) {
553  /* Step 2: grab an order */
554  auto order = m_fleet[position].orders()[orders.front()];
555  pgassert(order.idx() == orders.front());
556 
557 
558  /* Step 3:
559  * cycle the fleet
560  * insert in first truck possible
561  */
562 
563  for (size_t i = 0; i < position; ++i) {
564  m_fleet[i].hillClimb(order);
565  pgassert((m_fleet[i].has_order(order) && m_fleet[i].is_feasible()) || !m_fleet[i].has_order(order));
566  if (m_fleet[i].has_order(order)) {
567  /*
568  * delete the order from the current truck
569  */
570  m_fleet[position].erase(order);
571  break;
572  }
573  }
574  }
575  return m_fleet[position].orders_in_vehicle().empty();
576 }
577 
578 void
580  if (duration() < best_solution.duration()) {
581  best_solution = (*this);
582  msg().log << "\n*********** best by duration"
583  << best_solution.cost_str();
584 #if 0
585  msg().dbg_log << best_solution.tau("best by duration");
586 #endif
587  }
588  if (m_fleet.size() < best_solution.fleet().size()) {
589  best_solution = (*this);
590  msg().log << "\n*********** best by fleet size"
591  << best_solution.cost_str();
592 #if 0
593  msg().dbg_log << best_solution.tau("best by fleet size");
594 #endif
595  }
596 }
597 
598 
599 } // namespace simple
600 } // namespace optimizers
601 } // namespace vrprouting
vrprouting::problem::Solution
Definition: solution.h:50
vrprouting::problem::Solution::orders
const Orders & orders() const
Definition: solution.h:120
vrprouting::problem::Vehicle_pickDeliver::erase
void erase(const Order &order)
erases the order from the vehicle
Definition: vehicle_pickDeliver.cpp:154
vrprouting::optimizers::simple::Optimize::sort_for_move
void sort_for_move()
Definition: optimizers/simple.cpp:511
vrprouting::Pgr_messages::log
std::ostringstream log
Stores the hint information.
Definition: pgr_messages.h:91
vrprouting::problem::Solution::m_fleet
std::deque< Vehicle_pickDeliver > m_fleet
The current solution.
Definition: solution.h:125
vrprouting::initialsol::simple::Initials_code
Initials_code
Different kinds to insert an order into the vehicle.
Definition: initialsol/initials_code.h:37
simple.h
vrprouting::problem::Vehicle_pickDeliver::hillClimb
bool hillClimb(const Order &order)
Inserts an order with hill Climb approach.
Definition: vehicle_pickDeliver.cpp:444
vrprouting::optimizers::simple::Optimize::decrease_truck
void decrease_truck()
Definition: optimizers/simple.cpp:534
vrprouting::problem::Vehicle_pickDeliver::is_feasible
bool is_feasible() const
Definition: vehicle.cpp:120
vrprouting::problem::Vehicle::is_phony
bool is_phony() const
Definition: vehicle.cpp:116
vrprouting::optimizers::simple::Optimize::save_if_best
void save_if_best()
Definition: optimizers/simple.cpp:579
vrprouting::initialsol::simple::OneDepot
@ OneDepot
Push front order that allows more orders to be inserted at the front.
Definition: initialsol/initials_code.h:45
vrprouting::optimizers::simple::Optimize::sort_by_duration
void sort_by_duration()
Definition: optimizers/simple.cpp:354
pgassert
#define pgassert(expr)
Uses the standard assert syntax.
Definition: pgr_assert.h:95
vrprouting::problem::Vehicle_pickDeliver::has_order
bool has_order(const Order &order) const
does the vehicle has the order?
Definition: vehicle_pickDeliver.h:108
vrprouting::optimizers::simple::Optimize::get_kind
Initials_code get_kind() const
Definition: optimizers/simple.h:56
vrprouting::problem::Vehicle_pickDeliver::semiLIFO
bool semiLIFO(const Order &order)
Inserts an order In semi-Lifo (almost last in first out) order.
Definition: vehicle_pickDeliver.cpp:361
vrprouting::problem::Solution::duration
TInterval duration() const
Total duration of the solution.
Definition: solution.cpp:140
vrprouting::optimizers::simple::Optimize::move_reduce_cost
bool move_reduce_cost(problem::Vehicle_pickDeliver &from, problem::Vehicle_pickDeliver &to)
Definition: optimizers/simple.cpp:385
vrprouting::problem::Vehicle_pickDeliver::orders
const Orders & orders() const
Definition: vehicle_pickDeliver.h:148
vrprouting::optimizers::simple::Optimize::sort_by_id
void sort_by_id()
Definition: optimizers/simple.cpp:333
vrprouting::problem::Vehicle_pickDeliver
Definition: vehicle_pickDeliver.h:46
pgr_assert.h
An assert functionality that uses C++ throw().
vrprouting::problem::Solution::msg
Pgr_messages & msg()
Definition: solution.h:119
vrprouting::problem::Solution::tau
std::string tau(const std::string &title="Tau") const
writing the solution in compact form into a string
Definition: solution.cpp:116
vrprouting::optimizers::simple::Optimize::sort_by_size
void sort_by_size()
Definition: optimizers/simple.cpp:343
vrprouting::optimizers::simple::Optimize::Optimize
Optimize(const problem::Solution &solution, size_t times, const Initials_code &)
Definition: optimizers/simple.cpp:42
vrprouting::problem::Solution::is_feasible
bool is_feasible() const
is the solution feasible?
Definition: solution.cpp:129
vrprouting::optimizers::simple::Optimize::delete_empty_truck
void delete_empty_truck()
Definition: optimizers/simple.cpp:363
vrprouting::optimizers::simple::Optimize::swap_order
bool swap_order()
vrprouting::optimizers::simple::Optimize::swap_worse
bool swap_worse(problem::Vehicle_pickDeliver &from, problem::Vehicle_pickDeliver &to)
Definition: optimizers/simple.cpp:132
vrprouting::optimizers::simple::Optimize::inter_swap
bool inter_swap()
Definition: optimizers/simple.cpp:87
vrprouting::problem::Order
Definition: order.h:40
vrprouting::optimizers::simple::Optimize::move_order
bool move_order(problem::Order order, problem::Vehicle_pickDeliver &from_truck, problem::Vehicle_pickDeliver &to_truck)
moves an order to an non empty vehicle
Definition: optimizers/simple.cpp:467
vrprouting::optimizers::simple::Optimize::best_solution
Solution best_solution
Definition: optimizers/simple.h:54
vrprouting::problem::Vehicle_pickDeliver::empty
bool empty() const
Definition: vehicle.cpp:112
vrprouting::problem::Vehicle_pickDeliver::orders_in_vehicle
Identifiers< size_t > orders_in_vehicle() const
returns the number of orders in the vehicle
Definition: vehicle_pickDeliver.h:105
vrprouting
Definition: base_matrix.cpp:46