diff --git a/.gitignore b/.gitignore index 5fbd2cc63..9dcfeae37 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ bin/ lib/ build/ -.vscode \ No newline at end of file +.vscode +.idea \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 226861d1d..d887e1e84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [Unreleased] +## [v1.14.0] - 2024-01-16 ### Added @@ -48,6 +48,10 @@ - Remove unused `tw_length` member from `Job` and associated code - Scale `TimeWindow::length` from `UserDuration` to `Duration` (#1015) +#### Routing + +- ORS: (previously) hard-coded `/ors/v2` slug now has to be added to the path using `-a` (#1036) + #### Dependencies - Submodule and update Rapidjson (#929) @@ -72,6 +76,7 @@ - `max_travel_time` not accounted for with vehicle steps in solving mode (#954) - `max_travel_time` not accounted for in `RouteSplit` (#941) - Wrong capacity checks in `RouteSplit` (#981) +- Overflow related to scaling default time windows (#1020) #### Internals diff --git a/README.md b/README.md index 1fa77e4cb..bd983f824 100644 --- a/README.md +++ b/README.md @@ -134,10 +134,10 @@ solution quality and computing times. To cite VROOM in publications, please use: ```bibtex -@manual{vroom_v1.13, - title = {{VROOM v1.13, Vehicle Routing Open-source Optimization Machine}}, +@manual{vroom_v1.14, + title = {{VROOM v1.14, Vehicle Routing Open-source Optimization Machine}}, author = {Coupey, Julien and Nicod, Jean-Marc and Varnier, Christophe}, - year = 2023, + year = 2024, organization = {Verso (\url{https://verso-optim.com/})}, address = {Besançon, France}, note = {\url{http://vroom-project.org/}} diff --git a/docs/API.md b/docs/API.md index a200b9dc6..de8b5bcec 100644 --- a/docs/API.md +++ b/docs/API.md @@ -17,7 +17,7 @@ Contents: - all distances are in meters - a `time_window` object is a pair of timestamps in the form `[start, end]` - deprecated keys are crossed out -- `cost` values in output are the one used in the optimization objective (currently equal to `duration`) +- `cost` values in output are the one used internally in the optimization objective - a "task" is either a job, a pickup or a delivery # Solving mode @@ -420,6 +420,7 @@ Possible violation causes are: - "precedence" if a `shipment` precedence constraint is not met (`pickup` without matching `delivery`, `delivery` before/without matching `pickup`) - "missing_break" if a vehicle break has been omitted in its custom route - "max_travel_time" if the vehicle has more travel time than its `max_travel_time` value +- "max_distance" if the vehicle has a longer travel distance than its `max_distance` value - "max_load" if the load during a break exceed its `max_load` value Note on violations: reporting only really makes sense when using `-c` diff --git a/src/makefile b/src/makefile index 3fe861929..521596474 100644 --- a/src/makefile +++ b/src/makefile @@ -6,7 +6,7 @@ # Variables. CXX ?= g++ USE_ROUTING ?= true -CXXFLAGS = -MMD -MP -I. -std=c++20 -Wextra -Wpedantic -Wall -O3 -DASIO_STANDALONE -DUSE_ROUTING=$(USE_ROUTING) +CXXFLAGS = -MMD -MP -I. -std=c++20 -Wextra -Wpedantic -Wall -O3 -DASIO_STANDALONE -DUSE_ROUTING=$(USE_ROUTING) -DNDEBUG LDLIBS = -lpthread # Using all cpp files in current directory. diff --git a/src/problems/cvrp/operators/priority_replace.cpp b/src/problems/cvrp/operators/priority_replace.cpp index 48ab4cd90..54e249174 100644 --- a/src/problems/cvrp/operators/priority_replace.cpp +++ b/src/problems/cvrp/operators/priority_replace.cpp @@ -138,7 +138,8 @@ bool PriorityReplace::is_valid() { // portion is empty or with a single job (that would be an // UnassignedExchange move). replace_start_valid = - (s_rank > 0) && (_best_known_priority_gain <= _start_priority_gain) && + (0 < _start_priority_gain) && + (_best_known_priority_gain <= _start_priority_gain) && (s_rank > 0) && source.is_valid_addition_for_capacity_margins(_input, j.pickup, j.delivery, @@ -148,8 +149,9 @@ bool PriorityReplace::is_valid() { // Don't bother if the candidate end portion is empty or with a // single job (that would be an UnassignedExchange move). replace_end_valid = - (t_rank < s_route.size() - 1) && + (0 < _end_priority_gain) && (_best_known_priority_gain <= _end_priority_gain) && + (t_rank < s_route.size() - 1) && source.is_valid_addition_for_capacity_margins(_input, j.pickup, j.delivery, diff --git a/src/routing/http_wrapper.cpp b/src/routing/http_wrapper.cpp index 4ebaabd33..256e47a65 100644 --- a/src/routing/http_wrapper.cpp +++ b/src/routing/http_wrapper.cpp @@ -11,7 +11,9 @@ All rights reserved (see LICENSE). #include #include - +#include +#include +#include #include "routing/http_wrapper.h" using asio::ip::tcp; @@ -228,6 +230,11 @@ void HttpWrapper::add_geometry(Route& route) const { assert(get_legs_number(json_result) == non_break_locations.size() - 1); route.geometry = get_geometry(json_result); + + rapidjson::Document::AllocatorType& allocator = json_result.GetAllocator(); + route.legs = get_legs(json_result, allocator); + } + } // namespace vroom::routing diff --git a/src/routing/http_wrapper.h b/src/routing/http_wrapper.h index 062bb5ccf..f70cdcab8 100644 --- a/src/routing/http_wrapper.h +++ b/src/routing/http_wrapper.h @@ -70,6 +70,8 @@ class HttpWrapper : public Wrapper { virtual std::string get_geometry(rapidjson::Value& result) const = 0; + virtual rapidjson::Value get_legs(rapidjson::Value& result, rapidjson::Document::AllocatorType& allocator) const = 0; + void add_geometry(Route& route) const override; }; diff --git a/src/routing/ors_wrapper.cpp b/src/routing/ors_wrapper.cpp index 7c27900bc..6b7490319 100644 --- a/src/routing/ors_wrapper.cpp +++ b/src/routing/ors_wrapper.cpp @@ -48,8 +48,7 @@ std::string OrsWrapper::build_query(const std::vector& locations, body += "}"; // Building query for ORS - std::string query = - "POST /" + _server.path + "ors/v2/" + service + "/" + profile; + std::string query = "POST /" + _server.path + service + "/" + profile; query += " HTTP/1.0\r\n"; query += "Accept: */*\r\n"; @@ -98,5 +97,12 @@ unsigned OrsWrapper::get_legs_number(const rapidjson::Value& result) const { std::string OrsWrapper::get_geometry(rapidjson::Value& result) const { return result["routes"][0]["geometry"].GetString(); } + +rapidjson::Value OrsWrapper::get_legs(rapidjson::Value& result, rapidjson::Document::AllocatorType& allocator) const { + rapidjson::Value legs(rapidjson::kArrayType); + legs.CopyFrom(result["routes"][0]["segments"], allocator); + return legs; +} + } // namespace vroom::routing diff --git a/src/routing/ors_wrapper.h b/src/routing/ors_wrapper.h index dba31dd4f..8f134660c 100644 --- a/src/routing/ors_wrapper.h +++ b/src/routing/ors_wrapper.h @@ -38,6 +38,9 @@ class OrsWrapper : public HttpWrapper { unsigned get_legs_number(const rapidjson::Value& result) const override; std::string get_geometry(rapidjson::Value& result) const override; + + rapidjson::Value get_legs(rapidjson::Value& result, rapidjson::Document::AllocatorType& allocator) const override; + public: OrsWrapper(const std::string& profile, const Server& server); diff --git a/src/routing/osrm_routed_wrapper.cpp b/src/routing/osrm_routed_wrapper.cpp index b5859f36a..878289773 100644 --- a/src/routing/osrm_routed_wrapper.cpp +++ b/src/routing/osrm_routed_wrapper.cpp @@ -20,7 +20,7 @@ OsrmRoutedWrapper::OsrmRoutedWrapper(const std::string& profile, "durations", "distances", "route", - "alternatives=false&steps=false&overview=full&continue_" + "alternatives=false&steps=true&overview=full&continue_" "straight=false") { } @@ -116,5 +116,12 @@ OsrmRoutedWrapper::get_legs_number(const rapidjson::Value& result) const { std::string OsrmRoutedWrapper::get_geometry(rapidjson::Value& result) const { return result["routes"][0]["geometry"].GetString(); } + +rapidjson::Value OsrmRoutedWrapper::get_legs(rapidjson::Value& result, rapidjson::Document::AllocatorType& allocator) const { + rapidjson::Value legs(rapidjson::kArrayType); + legs.CopyFrom(result["routes"][0]["legs"], allocator); + return legs; +} + } // namespace vroom::routing diff --git a/src/routing/osrm_routed_wrapper.h b/src/routing/osrm_routed_wrapper.h index 483549bf5..538930655 100644 --- a/src/routing/osrm_routed_wrapper.h +++ b/src/routing/osrm_routed_wrapper.h @@ -38,8 +38,12 @@ class OsrmRoutedWrapper : public HttpWrapper { unsigned get_legs_number(const rapidjson::Value& result) const override; std::string get_geometry(rapidjson::Value& result) const override; + + rapidjson::Value get_legs(rapidjson::Value& result, rapidjson::Document::AllocatorType& allocator) const override; + public: + OsrmRoutedWrapper(const std::string& profile, const Server& server); }; diff --git a/src/routing/valhalla_wrapper.cpp b/src/routing/valhalla_wrapper.cpp index 00b175fa3..5b0ecd293 100644 --- a/src/routing/valhalla_wrapper.cpp +++ b/src/routing/valhalla_wrapper.cpp @@ -185,5 +185,12 @@ std::string ValhallaWrapper::get_geometry(rapidjson::Value& result) const { return encoder.encode(); } + +rapidjson::Value ValhallaWrapper::get_legs(rapidjson::Value& result, rapidjson::Document::AllocatorType& allocator) const { + rapidjson::Value legs(rapidjson::kArrayType); + legs.CopyFrom(result["trip"]["legs"], allocator); + return legs; +} + } // namespace vroom::routing diff --git a/src/routing/valhalla_wrapper.h b/src/routing/valhalla_wrapper.h index a5048786a..305d33b7f 100644 --- a/src/routing/valhalla_wrapper.h +++ b/src/routing/valhalla_wrapper.h @@ -43,6 +43,8 @@ class ValhallaWrapper : public HttpWrapper { std::string get_geometry(rapidjson::Value& result) const override; + rapidjson::Value get_legs(rapidjson::Value& result, rapidjson::Document::AllocatorType& allocator) const override; + public: ValhallaWrapper(const std::string& profile, const Server& server); }; diff --git a/src/structures/typedefs.h b/src/structures/typedefs.h index 79ef1ec15..ed70b0e5f 100644 --- a/src/structures/typedefs.h +++ b/src/structures/typedefs.h @@ -190,19 +190,22 @@ struct StringHash { }; namespace utils { -inline Duration scale_from_user_duration(UserDuration d) { +constexpr inline Duration scale_from_user_duration(UserDuration d) { return DURATION_FACTOR * static_cast(d); } -inline UserDuration scale_to_user_duration(Duration d) { +constexpr inline UserDuration scale_to_user_duration(Duration d) { + assert(d <= + scale_from_user_duration(std::numeric_limits::max())); return static_cast(d / DURATION_FACTOR); } -inline Cost scale_from_user_cost(UserCost c) { +constexpr inline Cost scale_from_user_cost(UserCost c) { return DURATION_FACTOR * COST_FACTOR * static_cast(c); } -inline UserCost scale_to_user_cost(Cost c) { +constexpr inline UserCost scale_to_user_cost(Cost c) { + assert(c <= scale_from_user_cost(std::numeric_limits::max())); return static_cast(c / (DURATION_FACTOR * COST_FACTOR)); } } // namespace utils diff --git a/src/structures/vroom/solution/route.h b/src/structures/vroom/solution/route.h index 113cdc782..b9bc94b5d 100644 --- a/src/structures/vroom/solution/route.h +++ b/src/structures/vroom/solution/route.h @@ -14,7 +14,7 @@ All rights reserved (see LICENSE). #include "structures/vroom/solution/step.h" #include "structures/vroom/solution/violations.h" - +#include "../include/rapidjson/include/rapidjson/document.h" namespace vroom { struct Route { @@ -34,6 +34,7 @@ struct Route { Violations violations; std::string geometry; + rapidjson::Value legs; Route(); diff --git a/src/structures/vroom/time_window.cpp b/src/structures/vroom/time_window.cpp index 69832f78a..487526c68 100644 --- a/src/structures/vroom/time_window.cpp +++ b/src/structures/vroom/time_window.cpp @@ -13,10 +13,13 @@ All rights reserved (see LICENSE). namespace vroom { constexpr Duration TimeWindow::default_length = - std::numeric_limits::max(); + utils::scale_from_user_duration(std::numeric_limits::max()); TimeWindow::TimeWindow() - : start(0), end(std::numeric_limits::max()), length(end - start) { + : start(0), + end(utils::scale_from_user_duration( + std::numeric_limits::max())), + length(end - start) { } TimeWindow::TimeWindow(UserDuration start, UserDuration end) diff --git a/src/structures/vroom/vehicle.cpp b/src/structures/vroom/vehicle.cpp index 0948175ee..aeeb0d66f 100644 --- a/src/structures/vroom/vehicle.cpp +++ b/src/structures/vroom/vehicle.cpp @@ -45,7 +45,8 @@ Vehicle::Vehicle(Id id, max_travel_time(max_travel_time.has_value() ? utils::scale_from_user_duration(max_travel_time.value()) : DEFAULT_MAX_TRAVEL_TIME), - max_distance(max_distance.value_or(DEFAULT_MAX_DISTANCE)), + max_distance(max_distance.has_value() ? max_distance.value() + : DEFAULT_MAX_DISTANCE), has_break_max_load(std::ranges::any_of(breaks, [](const auto& b) { return b.max_load.has_value(); })) { diff --git a/src/utils/output_json.cpp b/src/utils/output_json.cpp index db0414c34..e392f881a 100644 --- a/src/utils/output_json.cpp +++ b/src/utils/output_json.cpp @@ -263,6 +263,13 @@ rapidjson::Value to_json(const Route& route, route.geometry.size()); } + + if (!route.legs.Empty()) { + rapidjson::Value legs(rapidjson::kArrayType); + legs.CopyFrom(route.legs, allocator); + json_route.AddMember("legs", legs, allocator); +} + return json_route; } diff --git a/src/utils/version.h b/src/utils/version.h index 395a4447f..53482a449 100644 --- a/src/utils/version.h +++ b/src/utils/version.h @@ -15,7 +15,7 @@ All rights reserved (see LICENSE). constexpr unsigned MAJOR = 1; constexpr unsigned MINOR = 14; constexpr unsigned PATCH = 0; -constexpr bool DEV = true; +constexpr bool DEV = false; constexpr unsigned RC = 0; namespace vroom {