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