vrpRouting  0.3
optimize_driver.cpp
Go to the documentation of this file.
1 /*PGR-GNU*****************************************************************
2 File: optimize_driver.cpp
3 
4 Copyright (c) 2021 pgRouting developers
6 
7 Developer:
8 Copyright (c) 2021 Celia Virginia Vergara Castillo
9 Copyright (c) 2021 Joseph Emile Honour Percival
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*/
32 
33 #include <cstring>
34 #include <sstream>
35 #include <string>
36 #include <vector>
37 #include <utility>
38 #include <algorithm>
39 
40 #include "problem/pickDeliver.h"
43 #include "c_types/vehicle_t.h"
44 #include "problem/matrix.h"
45 
46 #include "cpp_common/pgr_assert.h"
48 #include "initialsol/tabu.h"
49 #include "optimizers/tabu.h"
50 #include "c_common/pgr_alloc.hpp"
52 
53 namespace {
54 
68 std::vector<Short_vehicle>
70  PickDeliveryOrders_t *shipments_arr, size_t total_shipments,
71  Vehicle_t *vehicles_arr, size_t total_vehicles,
72  std::vector<Short_vehicle> new_stops,
73  const vrprouting::problem::Matrix &time_matrix,
74  int max_cycles,
75  int64_t execution_date) {
76  try {
77  /*
78  * Construct problem
79  */
81  shipments_arr, total_shipments,
82  vehicles_arr, total_vehicles,
83  new_stops,
84  time_matrix);
85 
86  /*
87  * Unknown problem when createing the pick Deliver problem
88  */
89  if (!pd_problem.msg.get_error().empty()) {
90  throw std::make_pair(pd_problem.msg.get_error(), pd_problem.msg.get_log());
91  }
92 
93  /*
94  * get initial solution
95  */
96  using Initial_solution = vrprouting::initialsol::tabu::Initial_solution;
97  using Solution = vrprouting::problem::Solution;
98  auto sol = static_cast<Solution>(Initial_solution(execution_date, true, &pd_problem));
99 
100  /*
101  * Optimize the initial solution:
102  * - false: do not stop on all served
103  * - true: optimize
104  */
106  sol = Optimize(sol, static_cast<size_t>(max_cycles), false, true);
107 
108  return sol.get_stops();
109  } catch(...) {
110  throw;
111  }
112 }
113 
114 
124  PickDeliveryOrders_t *shipments_arr, size_t total_shipments
125  ) {
126  Identifiers<TTimestamp> processing_times;
127  for (size_t i = 0; i < total_shipments; ++i) {
128  processing_times += shipments_arr[i].pick_open_t;
129  processing_times += shipments_arr[i].pick_close_t;
130  processing_times += shipments_arr[i].deliver_open_t;
131  processing_times += shipments_arr[i].deliver_close_t;
132  }
133  return processing_times;
134 }
135 
145  Vehicle_t *vehicles_arr, size_t total_vehicles
146  ) {
147  Identifiers<TTimestamp> processing_times;
148  for (size_t i = 0; i < total_vehicles; ++i) {
149  processing_times += vehicles_arr[i].start_open_t;
150  processing_times += vehicles_arr[i].start_close_t;
151  processing_times += vehicles_arr[i].end_open_t;
152  processing_times += vehicles_arr[i].end_close_t;
153  }
154  return processing_times;
155 }
156 
164 std::vector<Short_vehicle>
166  Vehicle_t *vehicles_arr, size_t total_vehicles
167  ) {
168  std::vector<Short_vehicle> the_stops;
169  for (size_t i = 0; i < total_vehicles; ++i) {
170  std::vector<Id> stops;
171  for (size_t j = 0; j < vehicles_arr[i].stops_size; ++j) {
172  stops.push_back(vehicles_arr[i].stops[j]);
173  }
174  the_stops.push_back({vehicles_arr[i].id, stops});
175  }
176  std::sort(the_stops.begin(), the_stops.end(), []
177  (const Short_vehicle &lhs, const Short_vehicle &rhs) {return lhs.id < rhs.id;});
178  return the_stops;
179 }
180 
186 void
187 update_stops(std::vector<Short_vehicle>& the_stops, // NOLINT [runtime/references]
188  const std::vector<Short_vehicle> new_values) {
189  for (const auto &v : new_values) {
190  auto v_id = v.id;
191  auto v_to_modify = std::find_if(
192  the_stops.begin(), the_stops.end(), [v_id]
193  (const Short_vehicle& v) -> bool {return v.id == v_id;});
194  pgassert(v_to_modify != the_stops.end());
195  v_to_modify->stops = v.stops;
196  }
197 }
198 
212 std::vector<Short_vehicle>
214  PickDeliveryOrders_t *shipments_arr, size_t total_shipments,
215  Vehicle_t *vehicles_arr, size_t total_vehicles,
216  const vrprouting::problem::Matrix &time_matrix,
217  int max_cycles,
218  int64_t execution_date,
219  bool subdivide_by_vehicle,
220  std::ostringstream &log) {
221  try {
222  auto the_stops = get_initial_stops(vehicles_arr, total_vehicles);
223 
224  auto processing_times = subdivide_by_vehicle?
225  processing_times_by_vehicle(vehicles_arr, total_vehicles)
226  : processing_times_by_shipment(shipments_arr, total_shipments);
227 
228  Identifiers<Id> prev_shipments_in_stops;
229  for (const auto &t : processing_times) {
231  /*
232  * Get active vehicles at time t
233  */
234  auto vehicles_to_process = static_cast<size_t>(std::distance(vehicles_arr,
235  std::partition(
236  vehicles_arr, vehicles_arr + total_vehicles,
237  [&](const Vehicle_t& v)
238  {return v.start_open_t <= t && t <= v.end_close_t;})));
239 
240  /* Get shipments in stops of active vehicles */
241  Identifiers<Id> shipments_in_stops;
242  for (size_t i = 0; i < vehicles_to_process; ++i) {
243  auto v_id = vehicles_arr[i].id;
244  auto v_to_modify = std::find_if(
245  the_stops.begin(), the_stops.end(), [&]
246  (const Short_vehicle& v) -> bool {return v.id == v_id;});
247 
248  for (const auto &s : v_to_modify->stops) {
249  shipments_in_stops += s;
250  }
251  }
252 
253  /*
254  * Nothing to do:
255  * - no shipments to process
256  * - last optimization had exavtly the same shipments
257  */
258  if ((shipments_in_stops.size() == 0)
259  || (prev_shipments_in_stops == shipments_in_stops)) continue;
260  log << "\nOptimizing at time: " << t;
261 
262  prev_shipments_in_stops = shipments_in_stops;
263 
264  auto shipments_to_process = static_cast<size_t>(std::distance(shipments_arr,
265  std::partition(shipments_arr, shipments_arr + total_shipments,
266  [&](const PickDeliveryOrders_t& s){return shipments_in_stops.has(s.id);})));
267 
268  pgassert(shipments_to_process > 0);
269  pgassert(shipments_in_stops.size() == static_cast<size_t>(shipments_to_process));
270 
271  auto new_stops = one_processing(
272  shipments_arr, shipments_to_process,
273  vehicles_arr, vehicles_to_process, the_stops,
274  time_matrix,
275  max_cycles, execution_date);
276 
277  update_stops(the_stops, new_stops);
278  }
279 
280  return the_stops;
281  } catch(...) {
282  throw;
283  }
284 }
285 
286 
287 } // namespace
288 
349 void
351  PickDeliveryOrders_t *shipments_arr, size_t total_shipments,
352  Vehicle_t *vehicles_arr, size_t total_vehicles,
353  Matrix_cell_t *matrix_cells_arr, size_t total_cells,
354  Time_multipliers_t *multipliers_arr, size_t total_multipliers,
355 
356 
357  double factor,
358  int max_cycles,
359  int64_t execution_date,
360 
361  bool check_triangle_inequality,
362  bool subdivide,
363  bool subdivide_by_vehicle,
364 
365  Short_vehicle_rt **return_tuples,
366  size_t *return_count,
367 
368  char **log_msg,
369  char **notice_msg,
370  char **err_msg) {
371  std::ostringstream log;
372  std::ostringstream notice;
373  std::ostringstream err;
374  try {
375  /*
376  * verify preconditions
377  */
378  pgassert(!(*log_msg));
379  pgassert(!(*notice_msg));
380  pgassert(!(*err_msg));
381  pgassert(total_shipments);
382  pgassert(total_vehicles);
383  pgassert(total_cells);
384  pgassert(*return_count == 0);
385  pgassert(!(*return_tuples));
386 
387  *return_tuples = nullptr;
388  *return_count = 0;
389 
390  Identifiers<Id> node_ids;
391  Identifiers<Id> shipments_in_stops;
392 
393  /*
394  * Remove vehicles not going to be optimized and sort remaining vehicles
395  * 1. sort by id
396  * 2. remove duplicates
397  * - data comes from query that could possibly give a duplicate
398  * 3. remove vehicles that closes(end) before the execution time
399  */
400  std::sort(vehicles_arr, vehicles_arr + total_vehicles,
401  [](const Vehicle_t& lhs, const Vehicle_t& rhs){return lhs.id < rhs.id;});
402 
403  total_vehicles = static_cast<size_t>(std::distance(vehicles_arr,
404  std::unique(vehicles_arr, vehicles_arr + total_vehicles,
405  [&](const Vehicle_t& lhs, const Vehicle_t& rhs){return lhs.id == rhs.id;})));
406 
407  total_vehicles = static_cast<size_t>(std::distance(vehicles_arr,
408  std::remove_if(vehicles_arr, vehicles_arr + total_vehicles,
409  [&](const Vehicle_t& v){return v.end_close_t < execution_date;})));
410 
411  /*
412  * Remove shipments not involved in optimization
413  * 1. get the shipments on the stops of the vehicles
414  * - getting the node_ids in the same cycle
415  * 2. Remove duplicates
416  * 2. Remove shipments not on the stops
417  */
418  for (size_t i = 0; i < total_vehicles; ++i) {
419  node_ids += vehicles_arr[i].start_node_id;
420  node_ids += vehicles_arr[i].end_node_id;
421  for (size_t j = 0; j < vehicles_arr[i].stops_size; ++j) {
422  shipments_in_stops += vehicles_arr[i].stops[j];
423  }
424  }
425 
426  std::sort(shipments_arr, shipments_arr + total_shipments,
427  [](const PickDeliveryOrders_t& lhs, const PickDeliveryOrders_t& rhs){return lhs.id < rhs.id;});
428 
429  total_shipments = static_cast<size_t>(std::distance(shipments_arr,
430  std::unique(shipments_arr, shipments_arr + total_shipments,
431  [&](const PickDeliveryOrders_t& lhs, const PickDeliveryOrders_t& rhs){return lhs.id == rhs.id;})));
432 
433  total_shipments = static_cast<size_t>(std::distance(shipments_arr,
434  std::remove_if(shipments_arr, shipments_arr + total_shipments,
435  [&](const PickDeliveryOrders_t& s){return !shipments_in_stops.has(s.id);})));
436 
437  /*
438  * Verify shipments complete data
439  */
440  if (shipments_in_stops.size() != total_shipments) {
441  for (size_t i = 0; i < total_shipments; ++i) {
442  shipments_in_stops -= shipments_arr[i].id;
443  }
444  std::ostringstream hint;
445  err << "Missing shipments for processing ";
446  hint << "Shipments missing: " << shipments_in_stops << log.str();
447  *log_msg = pgr_msg(hint.str());
448  *err_msg = pgr_msg(err.str());
449  return;
450  }
451 
452  /*
453  * Finish getting the node ids involved on the process
454  */
455  for (size_t i = 0; i < total_shipments; ++i) {
456  node_ids += shipments_arr[i].pick_node_id;
457  node_ids += shipments_arr[i].deliver_node_id;
458  }
459 
460  /*
461  * Dealing with time matrix:
462  * - Create the unique time matrix to be used for all optimizations
463  * - Verify matrix triangle inequality
464  * - Verify matrix cells preconditions
465  */
466  vrprouting::problem::Matrix time_matrix(
467  matrix_cells_arr, total_cells,
468  multipliers_arr, total_multipliers,
469  node_ids, static_cast<Multiplier>(factor));
470 
471  if (check_triangle_inequality && !time_matrix.obeys_triangle_inequality()) {
472  log << "\nFixing Matrix that does not obey triangle inequality "
473  << time_matrix.fix_triangle_inequality() << " cycles used";
474 
475  if (!time_matrix.obeys_triangle_inequality()) {
476  log << "\nMatrix Still does not obey triangle inequality ";
477  }
478  }
479 
480  if (!time_matrix.has_no_infinity()) {
481  err << "\nAn Infinity value was found on the Matrix";
482  *err_msg = pgr_msg(err.str());
483  *log_msg = pgr_msg(log.str());
484  return;
485  }
486 
487  /*
488  * get the solution
489  */
490  auto solution = subdivide?
492  shipments_arr, total_shipments,
493  vehicles_arr, total_vehicles,
494  time_matrix,
495  max_cycles, execution_date,
496  subdivide_by_vehicle,
497  log) :
499  shipments_arr, total_shipments,
500  vehicles_arr, total_vehicles, {},
501  time_matrix,
502  max_cycles, execution_date);
503 
504  /*
505  * Prepare results
506  */
507  if (!solution.empty()) {
508  (*return_tuples) = pgr_alloc(total_shipments * 2, (*return_tuples));
509 
510  size_t seq = 0;
511  for (const auto &row : solution) {
512  for (const auto &o_id : row.stops) {
513  (*return_tuples)[seq].vehicle_id = row.id;
514  (*return_tuples)[seq].order_id = o_id;
515  ++seq;
516  }
517  }
518  }
519 
520  (*return_count) = total_shipments * 2;
521 
522  pgassert(*err_msg == nullptr);
523  *log_msg = log.str().empty()?
524  nullptr :
525  pgr_msg(log.str());
526  *notice_msg = notice.str().empty()?
527  nullptr :
528  pgr_msg(notice.str());
529  } catch (AssertFailedException &except) {
530  err << except.what() << log.str();
531  *err_msg = pgr_msg(err.str());
532  } catch (std::exception& except) {
533  err << except.what() << log.str();
534  *err_msg = pgr_msg(err.str());
535  } catch (const std::pair<std::string, std::string>& ex) {
536  err << ex.first;
537  log.str("");
538  log.clear();
539  log << ex.second;
540  *err_msg = pgr_msg(err.str().c_str());
541  *log_msg = pgr_msg(log.str().c_str());
542  } catch(...) {
543  err << "Caught unknown exception!" << log.str();
544  *err_msg = pgr_msg(err.str());
545  }
546 }
anonymous_namespace{optimize_driver.cpp}::processing_times_by_vehicle
Identifiers< TTimestamp > processing_times_by_vehicle(Vehicle_t *vehicles_arr, size_t total_vehicles)
: extract the times where the vehicle opens or closes
Definition: optimize_driver.cpp:144
PickDeliveryOrders_t::deliver_node_id
Id deliver_node_id
Deliver y coordinate: used in stand alone program for benchmarks.
Definition: pickDeliveryOrders_t.h:72
vrprouting::problem::Solution
Definition: solution.h:50
interruption.h
pgr_alloc
T * pgr_alloc(std::size_t size, T *ptr)
allocates memory
Definition: pgr_alloc.hpp:66
Vehicle_t::start_node_id
Id start_node_id
Stops size.
Definition: vehicle_t.h:58
pickDeliveryOrders_t.h
Vehicle_t::stops
Id * stops
Number of vehicles with same description.
Definition: vehicle_t.h:55
pgr_messages.h
vehicle_t.h
Vehicle_t::end_node_id
Id end_node_id
Definition: vehicle_t.h:65
pgr_msg
char * pgr_msg(const std::string &msg)
Definition: pgr_alloc.cpp:33
AssertFailedException::what
virtual const char * what() const
Definition: pgr_assert.cpp:67
anonymous_namespace{optimize_driver.cpp}::processing_times_by_shipment
Identifiers< TTimestamp > processing_times_by_shipment(PickDeliveryOrders_t *shipments_arr, size_t total_shipments)
: extract the times where the shipments opens or closes
Definition: optimize_driver.cpp:123
PickDeliveryOrders_t
order's attributes
Definition: pickDeliveryOrders_t.h:56
do_optimize
void do_optimize(PickDeliveryOrders_t *shipments_arr, size_t total_shipments, Vehicle_t *vehicles_arr, size_t total_vehicles, Matrix_cell_t *matrix_cells_arr, size_t total_cells, Time_multipliers_t *multipliers_arr, size_t total_multipliers, double factor, int max_cycles, int64_t execution_date, bool check_triangle_inequality, bool subdivide, bool subdivide_by_vehicle, Short_vehicle_rt **return_tuples, size_t *return_count, char **log_msg, char **notice_msg, char **err_msg)
Definition: optimize_driver.cpp:350
tabu.h
Time_multipliers_t
Time Dependant Multipliers.
Definition: time_multipliers_t.h:46
short_vehicle_rt.h
vrprouting::problem::PickDeliver
the pick deliver problem
Definition: pickDeliver.h:50
vrprouting::optimizers::tabu::Optimize
Class that optimizes a solution.
Definition: optimizers/tabu.h:51
anonymous_namespace{optimize_driver.cpp}::one_processing
std::vector< Short_vehicle > one_processing(PickDeliveryOrders_t *shipments_arr, size_t total_shipments, Vehicle_t *vehicles_arr, size_t total_vehicles, std::vector< Short_vehicle > new_stops, const vrprouting::problem::Matrix &time_matrix, int max_cycles, int64_t execution_date)
Executes an optimization with the input data.
Definition: optimize_driver.cpp:69
PickDeliveryOrders_t::deliver_close_t
TTimestamp deliver_close_t
Deliver open time.
Definition: pickDeliveryOrders_t.h:75
Vehicle_t
vehicles's attributes
Definition: vehicle_t.h:50
vrprouting::initialsol::tabu::Initial_solution
Definition: initialsol/tabu.h:45
Vehicle_t::start_open_t
TTimestamp start_open_t
Start node's identifier.
Definition: vehicle_t.h:59
pgassert
#define pgassert(expr)
Uses the standard assert syntax.
Definition: pgr_assert.h:95
Vehicle_t::end_open_t
TTimestamp end_open_t
End node's identifier.
Definition: vehicle_t.h:66
tabu.h
anonymous_namespace{optimize_driver.cpp}::subdivide_processing
std::vector< Short_vehicle > subdivide_processing(PickDeliveryOrders_t *shipments_arr, size_t total_shipments, Vehicle_t *vehicles_arr, size_t total_vehicles, const vrprouting::problem::Matrix &time_matrix, int max_cycles, int64_t execution_date, bool subdivide_by_vehicle, std::ostringstream &log)
Executes an optimization by subdivision of data.
Definition: optimize_driver.cpp:213
matrix.h
Identifiers::size
size_t size() const
Definition: identifiers.hpp:77
vrprouting::problem::PickDeliver::msg
Pgr_messages msg
message controller for all classes
Definition: pickDeliver.h:99
pgr_alloc.hpp
vrprouting::problem::Matrix
Definition: matrix.h:46
PickDeliveryOrders_t::id
Id id
Definition: pickDeliveryOrders_t.h:59
PickDeliveryOrders_t::pick_node_id
Id pick_node_id
Pick y coordinate: used in stand alone program for benchmarks.
Definition: pickDeliveryOrders_t.h:64
Short_vehicle
short_vehicle
Definition: short_vehicle.h:41
PickDeliveryOrders_t::pick_open_t
TTimestamp pick_open_t
Pickup node identifier.
Definition: pickDeliveryOrders_t.h:66
vrprouting::Pgr_messages::get_log
std::string get_log() const
gets the contents of log message
Definition: pgr_messages.cpp:36
pickDeliver.h
Short_vehicle::stops
std::vector< Id > stops
Vehicle's identifier.
Definition: short_vehicle.h:43
Vehicle_t::start_close_t
TTimestamp start_close_t
Start open time.
Definition: vehicle_t.h:60
pgr_assert.h
An assert functionality that uses C++ throw().
vrprouting::base::Base_Matrix::has_no_infinity
bool has_no_infinity() const
does the matrix values not given by the user?
Definition: base_matrix.cpp:459
vrprouting::base::Base_Matrix::fix_triangle_inequality
size_t fix_triangle_inequality(size_t depth=0)
Fix Triangle Inequality Theorem.
Definition: base_matrix.cpp:508
anonymous_namespace{optimize_driver.cpp}::update_stops
void update_stops(std::vector< Short_vehicle > &the_stops, const std::vector< Short_vehicle > new_values)
Update the vehicle stops to the new values.
Definition: optimize_driver.cpp:187
anonymous_namespace{optimize_driver.cpp}::get_initial_stops
std::vector< Short_vehicle > get_initial_stops(Vehicle_t *vehicles_arr, size_t total_vehicles)
get the original stops
Definition: optimize_driver.cpp:165
Short_vehicle_rt
short_vehicle
Definition: short_vehicle_rt.h:40
vrprouting::Pgr_messages::get_error
std::string get_error() const
gets the contents of error message
Definition: pgr_messages.cpp:53
Matrix_cell_t
traveling costs
Definition: matrix_cell_t.h:41
PickDeliveryOrders_t::deliver_open_t
TTimestamp deliver_open_t
Deliver node identifier.
Definition: pickDeliveryOrders_t.h:74
CHECK_FOR_INTERRUPTS
#define CHECK_FOR_INTERRUPTS()
Definition: interruption.h:79
Identifiers::has
bool has(const T other) const
true ids() has element
Definition: identifiers.hpp:100
Vehicle_t::end_close_t
TTimestamp end_close_t
End open time.
Definition: vehicle_t.h:67
PickDeliveryOrders_t::pick_close_t
TTimestamp pick_close_t
Pickup open time.
Definition: pickDeliveryOrders_t.h:67
optimize_driver.h
Multiplier
double Multiplier
Definition: typedefs.h:77
Vehicle_t::stops_size
size_t stops_size
Stops.
Definition: vehicle_t.h:56
Vehicle_t::id
Id id
Definition: vehicle_t.h:51
Identifiers
Definition: identifiers.hpp:51
vrprouting::base::Base_Matrix::obeys_triangle_inequality
bool obeys_triangle_inequality() const
does the matrix obeys the triangle inequality?
Definition: base_matrix.cpp:487
AssertFailedException
Extends std::exception and is the exception that we throw if an assert fails.
Definition: pgr_assert.h:140