diff --git a/Dependencies.txt b/Dependencies.txt index 780323c990..7ac170bd21 100644 --- a/Dependencies.txt +++ b/Dependencies.txt @@ -1,11 +1,11 @@ -Protobuf=v24.4 -abseil-cpp=20230802.0 +Protobuf=v25.0 +abseil-cpp=20230802.1 Cbc=2.10.7 Cgl=0.60.5 Clp=1.17.7 Osi=0.108.7 CoinUtils=2.11.6 Eigen=3.4.0 -Re2=2023-09-01 +Re2=2023-11-01 HiGHS=v1.6.0 Scip=v804 diff --git a/WORKSPACE b/WORKSPACE index 0edbabed6a..8cb819517d 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -84,15 +84,15 @@ new_git_repository( ## Re2 git_repository( name = "com_google_re2", - tag = "2023-09-01", + tag = "2023-11-01", remote = "https://github.com/google/re2.git", ) ## Abseil-cpp git_repository( name = "com_google_absl", - tag = "20230802.0", - patches = ["//patches:abseil-cpp-20230802.0.patch"], + tag = "20230802.1", + patches = ["//patches:abseil-cpp-20230802.1.patch"], patch_args = ["-p1"], remote = "https://github.com/abseil/abseil-cpp.git", ) @@ -100,8 +100,8 @@ git_repository( ## Protobuf git_repository( name = "com_google_protobuf", - tag = "v24.4", - patches = ["//patches:protobuf-v24.4.patch"], + tag = "v25.0", + patches = ["//patches:protobuf-v25.0.patch"], patch_args = ["-p1"], remote = "https://github.com/protocolbuffers/protobuf.git", ) diff --git a/bazel/notebook_requirements.in b/bazel/notebook_requirements.in index 8d7a684930..2da78f85be 100644 --- a/bazel/notebook_requirements.in +++ b/bazel/notebook_requirements.in @@ -1,7 +1,7 @@ # OR-Tools code dependencies absl-py==2.0.0 numpy==1.26.1 -protobuf==4.24.4 +protobuf==4.25.0 scipy==1.11.3 # OR-Tools build dependencies diff --git a/bazel/notebook_requirements.txt b/bazel/notebook_requirements.txt index b3d70f2828..dda0ff00ba 100644 --- a/bazel/notebook_requirements.txt +++ b/bazel/notebook_requirements.txt @@ -228,7 +228,7 @@ prompt-toolkit==3.0.39 # via # ipython # jupyter-console -protobuf==4.24.4 +protobuf==4.25.0 # via # -r bazel/notebook_requirements.in # mypy-protobuf diff --git a/bazel/ortools_requirements.in b/bazel/ortools_requirements.in index 5a102563b6..f1ec189cda 100644 --- a/bazel/ortools_requirements.in +++ b/bazel/ortools_requirements.in @@ -1,7 +1,7 @@ # OR-Tools code dependencies absl-py==2.0.0 numpy==1.26.1 -protobuf==4.24.4 +protobuf==4.25.0 scipy==1.11.3 # OR-Tools build dependencies diff --git a/bazel/ortools_requirements.txt b/bazel/ortools_requirements.txt index 4e47810c3d..f7210a0e5e 100644 --- a/bazel/ortools_requirements.txt +++ b/bazel/ortools_requirements.txt @@ -37,7 +37,7 @@ platformdirs==3.10.0 # via # black # virtualenv -protobuf==4.24.4 +protobuf==4.25.0 # via # -r bazel/ortools_requirements.in # mypy-protobuf diff --git a/cmake/dependencies/CMakeLists.txt b/cmake/dependencies/CMakeLists.txt index 409fa29f2b..af3a28c113 100644 --- a/cmake/dependencies/CMakeLists.txt +++ b/cmake/dependencies/CMakeLists.txt @@ -82,8 +82,8 @@ if(BUILD_absl) FetchContent_Declare( absl GIT_REPOSITORY "https://github.com/abseil/abseil-cpp.git" - GIT_TAG "20230802.0" - PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/abseil-cpp-20230802.0.patch" + GIT_TAG "20230802.1" + PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/abseil-cpp-20230802.1.patch" ) FetchContent_MakeAvailable(absl) list(POP_BACK CMAKE_MESSAGE_INDENT) @@ -103,9 +103,9 @@ if(BUILD_Protobuf) FetchContent_Declare( protobuf GIT_REPOSITORY "https://github.com/protocolbuffers/protobuf.git" - GIT_TAG "v24.4" + GIT_TAG "v25.0" GIT_SUBMODULES "" - PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/protobuf-v24.4.patch") + PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/protobuf-v25.0.patch") FetchContent_MakeAvailable(protobuf) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") @@ -121,8 +121,8 @@ if(BUILD_re2) FetchContent_Declare( re2 GIT_REPOSITORY "https://github.com/google/re2.git" - GIT_TAG "2023-09-01" - #PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/re2-2023-09-01.patch" + GIT_TAG "2023-11-01" + #PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/re2-2023-11-01.patch" ) FetchContent_MakeAvailable(re2) list(POP_BACK CMAKE_MESSAGE_INDENT) diff --git a/cmake/dotnet.cmake b/cmake/dotnet.cmake index d2d1fbc2a2..a4e0ad3324 100644 --- a/cmake/dotnet.cmake +++ b/cmake/dotnet.cmake @@ -318,9 +318,9 @@ add_custom_command( add_custom_command( OUTPUT ${DOTNET_NATIVE_PROJECT_DIR}/timestamp COMMAND ${CMAKE_COMMAND} -E env --unset=TARGETNAME - ${DOTNET_EXECUTABLE} build --nologo -c Release /p:Platform=${DOTNET_PLATFORM} ${DOTNET_NATIVE_PROJECT}.csproj + ${DOTNET_EXECUTABLE} build --nologo -c Release -p:Platform=${DOTNET_PLATFORM} ${DOTNET_NATIVE_PROJECT}.csproj COMMAND ${CMAKE_COMMAND} -E env --unset=TARGETNAME - ${DOTNET_EXECUTABLE} pack --nologo -c Release ${DOTNET_NATIVE_PROJECT}.csproj + ${DOTNET_EXECUTABLE} pack --nologo -c Release -p:Platform=${DOTNET_PLATFORM} ${DOTNET_NATIVE_PROJECT}.csproj COMMAND ${CMAKE_COMMAND} -E touch ${DOTNET_NATIVE_PROJECT_DIR}/timestamp DEPENDS ${PROJECT_BINARY_DIR}/dotnet/Directory.Build.props @@ -344,11 +344,13 @@ add_custom_target(dotnet_native_package ## .Net Package ## #################### if(UNIVERSAL_DOTNET_PACKAGE) + set(DOTNET_META_PLATFORM any) configure_file( ${PROJECT_SOURCE_DIR}/ortools/dotnet/${DOTNET_PROJECT}-full.csproj.in ${DOTNET_PROJECT_DIR}/${DOTNET_PROJECT}.csproj.in @ONLY) else() + set(DOTNET_META_PLATFORM ${DOTNET_PLATFORM}) configure_file( ${PROJECT_SOURCE_DIR}/ortools/dotnet/${DOTNET_PROJECT}-local.csproj.in ${DOTNET_PROJECT_DIR}/${DOTNET_PROJECT}.csproj.in @@ -365,9 +367,9 @@ add_custom_command( add_custom_command( OUTPUT ${DOTNET_PROJECT_DIR}/timestamp COMMAND ${CMAKE_COMMAND} -E env --unset=TARGETNAME - ${DOTNET_EXECUTABLE} build --nologo -c Release /p:Platform=${DOTNET_PLATFORM} ${DOTNET_PROJECT}.csproj + ${DOTNET_EXECUTABLE} build --nologo -c Release -p:Platform=${DOTNET_META_PLATFORM} ${DOTNET_PROJECT}.csproj COMMAND ${CMAKE_COMMAND} -E env --unset=TARGETNAME - ${DOTNET_EXECUTABLE} pack --nologo -c Release ${DOTNET_PROJECT}.csproj + ${DOTNET_EXECUTABLE} pack --nologo -c Release -p:Platform=${DOTNET_META_PLATFORM} ${DOTNET_PROJECT}.csproj COMMAND ${CMAKE_COMMAND} -E touch ${DOTNET_PROJECT_DIR}/timestamp DEPENDS ${PROJECT_BINARY_DIR}/dotnet/or-tools.snk diff --git a/cmake/host.CMakeLists.txt b/cmake/host.CMakeLists.txt index 4df69dd859..ecafb2748d 100644 --- a/cmake/host.CMakeLists.txt +++ b/cmake/host.CMakeLists.txt @@ -105,8 +105,8 @@ set(ABSL_PROPAGATE_CXX_STD ON) FetchContent_Declare( absl GIT_REPOSITORY "https://github.com/abseil/abseil-cpp.git" - GIT_TAG "20230802.0" - PATCH_COMMAND git apply "${CMAKE_CURRENT_LIST_DIR}/@PATCHES_PATH@/abseil-cpp-20230802.0.patch") + GIT_TAG "20230802.1" + PATCH_COMMAND git apply "${CMAKE_CURRENT_LIST_DIR}/@PATCHES_PATH@/abseil-cpp-20230802.1.patch") FetchContent_MakeAvailable(absl) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") @@ -121,9 +121,9 @@ set(protobuf_WITH_ZLIB OFF) FetchContent_Declare( protobuf GIT_REPOSITORY "https://github.com/protocolbuffers/protobuf.git" - GIT_TAG "v24.4" + GIT_TAG "v25.0" GIT_SUBMODULES "" - PATCH_COMMAND git apply "${CMAKE_CURRENT_LIST_DIR}/@PATCHES_PATH@/protobuf-v24.4.patch") + PATCH_COMMAND git apply "${CMAKE_CURRENT_LIST_DIR}/@PATCHES_PATH@/protobuf-v25.0.patch") FetchContent_MakeAvailable(protobuf) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") diff --git a/ortools/constraint_solver/routing.cc b/ortools/constraint_solver/routing.cc index 45e6bfcc9e..65081ced0f 100644 --- a/ortools/constraint_solver/routing.cc +++ b/ortools/constraint_solver/routing.cc @@ -3970,7 +3970,11 @@ void RoutingModel::CreateNeighborhoodOperators( CreateCPOperator(); std::vector> alternative_sets(disjunctions_.size()); for (const RoutingModel::Disjunction& disjunction : disjunctions_) { - alternative_sets.push_back(disjunction.indices); + // Only add disjunctions of cardinality 1, as + // SwapActiveToShortestPathOperator only supports DAGs. + if (disjunction.value.max_cardinality == 1) { + alternative_sets.push_back(disjunction.indices); + } } local_search_operators_[SHORTEST_PATH_SWAP_ACTIVE] = CreateOperator( diff --git a/ortools/constraint_solver/routing_neighborhoods.cc b/ortools/constraint_solver/routing_neighborhoods.cc index e1fb01a9ec..e630ab3203 100644 --- a/ortools/constraint_solver/routing_neighborhoods.cc +++ b/ortools/constraint_solver/routing_neighborhoods.cc @@ -130,7 +130,8 @@ SwapActiveToShortestPathOperator::SwapActiveToShortestPathOperator( arc_evaluator_(std::move(arc_evaluator)), alternative_sets_(std::move(alternative_sets)), to_alternative_set_(vars.size(), -1), - path_predecessor_(vars.size(), -1) { + path_predecessor_(vars.size(), -1), + touched_(vars.size()) { for (int i = 0; i < alternative_sets_.size(); ++i) { for (int j : alternative_sets_[i]) { if (j < to_alternative_set_.size()) to_alternative_set_[j] = i; @@ -149,10 +150,10 @@ bool SwapActiveToShortestPathOperator::MakeNeighbor() { next = Next(next); } if (alternatives.empty()) return false; + const int sink = next; next = OldNext(before_chain); bool swap_done = false; - UpdateShortestPath(before_chain, next, alternatives); - for (int64_t node : path_) { + for (int64_t node : GetShortestPath(before_chain, sink, alternatives)) { if (node != next) { SwapActiveAndInactive(next, node); swap_done = true; @@ -162,10 +163,10 @@ bool SwapActiveToShortestPathOperator::MakeNeighbor() { return swap_done; } -void SwapActiveToShortestPathOperator::UpdateShortestPath( +const std::vector& SwapActiveToShortestPathOperator::GetShortestPath( int source, int sink, const std::vector& alternative_chain) { path_.clear(); - if (alternative_chain.empty()) return; + if (alternative_chain.empty()) return path_; // Initializing values at the first "layer" after the source (from source to // all alternatives at rank 0). const std::vector& first_alternative_set = @@ -219,12 +220,20 @@ void SwapActiveToShortestPathOperator::UpdateShortestPath( predecessor = last_alternative_set[alternative]; } } - if (predecessor == -1) return; + if (predecessor == -1) return path_; // Build the path from predecessors on the shortest path. path_.resize(alternative_chain.size(), predecessor); + touched_.SparseClearAll(); + touched_.Set(predecessor); for (int rank = alternative_chain.size() - 2; rank >= 0; --rank) { path_[rank] = path_predecessor_[path_[rank + 1]]; + if (touched_[path_[rank]]) { + path_.clear(); + return path_; + } + touched_.Set(path_[rank]); } + return path_; } MakePairActiveOperator::MakePairActiveOperator( diff --git a/ortools/constraint_solver/routing_neighborhoods.h b/ortools/constraint_solver/routing_neighborhoods.h index 6955aed8ae..ad3458cadf 100644 --- a/ortools/constraint_solver/routing_neighborhoods.h +++ b/ortools/constraint_solver/routing_neighborhoods.h @@ -24,6 +24,7 @@ #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/constraint_solver/routing_types.h" +#include "ortools/util/bitset.h" namespace operations_research { @@ -124,14 +125,15 @@ class SwapActiveToShortestPathOperator : public PathOperator { } private: - void UpdateShortestPath(int source, int sink, - const std::vector& alternative_chain); + const std::vector& GetShortestPath( + int source, int sink, const std::vector& alternative_chain); RoutingTransitCallback2 arc_evaluator_; const std::vector> alternative_sets_; std::vector to_alternative_set_; std::vector path_predecessor_; std::vector path_; + SparseBitset touched_; }; /// Pair-based neighborhood operators, designed to move nodes by pairs (pairs diff --git a/ortools/constraint_solver/samples/cvrp.py b/ortools/constraint_solver/samples/cvrp.py deleted file mode 100755 index aa266700a3..0000000000 --- a/ortools/constraint_solver/samples/cvrp.py +++ /dev/null @@ -1,194 +0,0 @@ -#!/usr/bin/env python3 -# This Python file uses the following encoding: utf-8 -# Copyright 2015 Tin Arm Engineering AB -# Copyright 2018 Google LLC -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Capacitated Vehicle Routing Problem (CVRP). - - This is a sample using the routing library python wrapper to solve a CVRP - problem. - A description of the problem can be found here: - http://en.wikipedia.org/wiki/Vehicle_routing_problem. - - Distances are in meters. -""" - -from functools import partial - -from ortools.constraint_solver import pywrapcp -from ortools.constraint_solver import routing_enums_pb2 - - -########################### -# Problem Data Definition # -########################### -def create_data_model(): - """Stores the data for the problem""" - data = {} - # Locations in block unit - _locations = \ - [(4, 4), # depot - (2, 0), (8, 0), # locations to visit - (0, 1), (1, 1), - (5, 2), (7, 2), - (3, 3), (6, 3), - (5, 5), (8, 5), - (1, 6), (2, 6), - (3, 7), (6, 7), - (0, 8), (7, 8)] - # Compute locations in meters using the block dimension defined as follow - # Manhattan average block: 750ft x 264ft -> 228m x 80m - # here we use: 114m x 80m city block - # src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance' - data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations] - data['num_locations'] = len(data['locations']) - data['demands'] = \ - [0, # depot - 1, 1, # 1, 2 - 2, 4, # 3, 4 - 2, 4, # 5, 6 - 8, 8, # 7, 8 - 1, 2, # 9,10 - 1, 2, # 11,12 - 4, 4, # 13, 14 - 8, 8] # 15, 16 - data['num_vehicles'] = 4 - data['vehicle_capacity'] = 15 - data['depot'] = 0 - return data - - -####################### -# Problem Constraints # -####################### -def manhattan_distance(position_1, position_2): - """Computes the Manhattan distance between two points""" - return (abs(position_1[0] - position_2[0]) + - abs(position_1[1] - position_2[1])) - - -def create_distance_evaluator(data): - """Creates callback to return distance between points.""" - _distances = {} - # precompute distance between location to have distance callback in O(1) - for from_node in range(data['num_locations']): - _distances[from_node] = {} - for to_node in range(data['num_locations']): - if from_node == to_node: - _distances[from_node][to_node] = 0 - else: - _distances[from_node][to_node] = (manhattan_distance( - data['locations'][from_node], data['locations'][to_node])) - - def distance_evaluator(manager, from_node, to_node): - """Returns the manhattan distance between the two nodes""" - return _distances[manager.IndexToNode(from_node)][manager.IndexToNode( - to_node)] - - return distance_evaluator - - -def create_demand_evaluator(data): - """Creates callback to get demands at each location.""" - _demands = data['demands'] - - def demand_evaluator(manager, node): - """Returns the demand of the current node""" - return _demands[manager.IndexToNode(node)] - - return demand_evaluator - - -def add_capacity_constraints(routing, data, demand_evaluator_index): - """Adds capacity constraint""" - capacity = 'Capacity' - routing.AddDimension( - demand_evaluator_index, - 0, # null capacity slack - data['vehicle_capacity'], - True, # start cumul to zero - capacity) - - -########### -# Printer # -########### -def print_solution(data, routing, manager, assignment): # pylint:disable=too-many-locals - """Prints assignment on console""" - print(f'Objective: {assignment.ObjectiveValue()}') - total_distance = 0 - total_load = 0 - capacity_dimension = routing.GetDimensionOrDie('Capacity') - for vehicle_id in range(data['num_vehicles']): - index = routing.Start(vehicle_id) - plan_output = f'Route for vehicle {vehicle_id}:\n' - distance = 0 - while not routing.IsEnd(index): - load_var = capacity_dimension.CumulVar(index) - plan_output += (f' {manager.IndexToNode(index)} ' - f'Load({assignment.Value(load_var)}) -> ') - previous_index = index - index = assignment.Value(routing.NextVar(index)) - distance += routing.GetArcCostForVehicle(previous_index, index, - vehicle_id) - load_var = capacity_dimension.CumulVar(index) - plan_output += f' {manager.IndexToNode(index)} Load({assignment.Value(load_var)})\n' - plan_output += f'Distance of the route: {distance}m\n' - plan_output += f'Load of the route: {assignment.Value(load_var)}\n' - print(plan_output) - total_distance += distance - total_load += assignment.Value(load_var) - print(f'Total Distance of all routes: {total_distance}m') - print(f'Total Load of all routes: {total_load}') - - -######## -# Main # -######## -def main(): - """Entry point of the program""" - # Instantiate the data problem. - data = create_data_model() - - # Create the routing index manager - manager = pywrapcp.RoutingIndexManager(data['num_locations'], - data['num_vehicles'], data['depot']) - - # Create Routing Model - routing = pywrapcp.RoutingModel(manager) - - # Define weight of each edge - distance_evaluator = routing.RegisterTransitCallback( - partial(create_distance_evaluator(data), manager)) - routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator) - - # Add Capacity constraint - demand_evaluator_index = routing.RegisterUnaryTransitCallback( - partial(create_demand_evaluator(data), manager)) - add_capacity_constraints(routing, data, demand_evaluator_index) - - # Setting first solution heuristic (cheapest addition). - search_parameters = pywrapcp.DefaultRoutingSearchParameters() - search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member - search_parameters.local_search_metaheuristic = ( - routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH) - search_parameters.time_limit.FromSeconds(1) - - # Solve the problem. - assignment = routing.SolveWithParameters(search_parameters) - print_solution(data, routing, manager, assignment) - - -if __name__ == '__main__': - main() diff --git a/ortools/constraint_solver/samples/cvrptw.py b/ortools/constraint_solver/samples/cvrptw.py deleted file mode 100755 index 03002a8812..0000000000 --- a/ortools/constraint_solver/samples/cvrptw.py +++ /dev/null @@ -1,320 +0,0 @@ -#!/usr/bin/env python3 -# This Python file uses the following encoding: utf-8 -# Copyright 2015 Tin Arm Engineering AB -# Copyright 2018 Google LLC -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# [START program] -"""Capacitated Vehicle Routing Problem with Time Windows (CVRPTW). - - This is a sample using the routing library python wrapper to solve a CVRPTW - problem. - A description of the problem can be found here: - http://en.wikipedia.org/wiki/Vehicle_routing_problem. - - Distances are in meters and time in minutes. -""" - -# [START import] -from functools import partial -from ortools.constraint_solver import routing_enums_pb2 -from ortools.constraint_solver import pywrapcp -# [END import] - - -# [START data_model] -def create_data_model(): - """Stores the data for the problem.""" - data = {} - # Locations in block unit - _locations = \ - [(4, 4), # depot - (2, 0), (8, 0), # locations to visit - (0, 1), (1, 1), - (5, 2), (7, 2), - (3, 3), (6, 3), - (5, 5), (8, 5), - (1, 6), (2, 6), - (3, 7), (6, 7), - (0, 8), (7, 8)] - # Compute locations in meters using the block dimension defined as follow - # Manhattan average block: 750ft x 264ft -> 228m x 80m - # here we use: 114m x 80m city block - # src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance" - data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations] - data['num_locations'] = len(data['locations']) - data['time_windows'] = \ - [(0, 0), - (75, 85), (75, 85), # 1, 2 - (60, 70), (45, 55), # 3, 4 - (0, 8), (50, 60), # 5, 6 - (0, 10), (10, 20), # 7, 8 - (0, 10), (75, 85), # 9, 10 - (85, 95), (5, 15), # 11, 12 - (15, 25), (10, 20), # 13, 14 - (45, 55), (30, 40)] # 15, 16 - data['demands'] = \ - [0, # depot - 1, 1, # 1, 2 - 2, 4, # 3, 4 - 2, 4, # 5, 6 - 8, 8, # 7, 8 - 1, 2, # 9,10 - 1, 2, # 11,12 - 4, 4, # 13, 14 - 8, 8] # 15, 16 - data['time_per_demand_unit'] = 5 # 5 minutes/unit - data['num_vehicles'] = 4 - data['vehicle_capacity'] = 15 - data['vehicle_speed'] = 83 # Travel speed: 5km/h converted in m/min - data['depot'] = 0 - return data - # [END data_model] - - -####################### -# Problem Constraints # -####################### -def manhattan_distance(position_1, position_2): - """Computes the Manhattan distance between two points""" - return (abs(position_1[0] - position_2[0]) + - abs(position_1[1] - position_2[1])) - - -def create_distance_evaluator(data): - """Creates callback to return distance between points.""" - _distances = {} - # precompute distance between location to have distance callback in O(1) - for from_node in range(data['num_locations']): - _distances[from_node] = {} - for to_node in range(data['num_locations']): - if from_node == to_node: - _distances[from_node][to_node] = 0 - else: - _distances[from_node][to_node] = (manhattan_distance( - data['locations'][from_node], data['locations'][to_node])) - - def distance_evaluator(manager, from_node, to_node): - """Returns the manhattan distance between the two nodes""" - return _distances[manager.IndexToNode(from_node)][manager.IndexToNode( - to_node)] - - return distance_evaluator - - -def create_demand_evaluator(data): - """Creates callback to get demands at each location.""" - _demands = data['demands'] - - def demand_evaluator(manager, node): - """Returns the demand of the current node""" - return _demands[manager.IndexToNode(node)] - - return demand_evaluator - - -def add_capacity_constraints(routing, data, demand_evaluator_index): - """Adds capacity constraint""" - capacity = 'Capacity' - routing.AddDimension( - demand_evaluator_index, - 0, # null capacity slack - data['vehicle_capacity'], - True, # start cumul to zero - capacity) - - -def create_time_evaluator(data): - """Creates callback to get total times between locations.""" - - def service_time(data, node): - """Gets the service time for the specified location.""" - return data['demands'][node] * data['time_per_demand_unit'] - - def travel_time(data, from_node, to_node): - """Gets the travel times between two locations.""" - if from_node == to_node: - travel_time = 0 - else: - travel_time = manhattan_distance( - data['locations'][from_node], - data['locations'][to_node]) / data['vehicle_speed'] - return travel_time - - _total_time = {} - # precompute total time to have time callback in O(1) - for from_node in range(data['num_locations']): - _total_time[from_node] = {} - for to_node in range(data['num_locations']): - if from_node == to_node: - _total_time[from_node][to_node] = 0 - else: - _total_time[from_node][to_node] = int( - service_time(data, from_node) + - travel_time(data, from_node, to_node)) - - def time_evaluator(manager, from_node, to_node): - """Returns the total time between the two nodes""" - return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode( - to_node)] - - return time_evaluator - - -def add_time_window_constraints(routing, manager, data, time_evaluator_index): - """Add Global Span constraint""" - time = 'Time' - horizon = 120 - routing.AddDimension( - time_evaluator_index, - horizon, # allow waiting time - horizon, # maximum time per vehicle - False, # don't force start cumul to zero since we are giving TW to start nodes - time) - time_dimension = routing.GetDimensionOrDie(time) - # Add time window constraints for each location except depot - # and 'copy' the slack var in the solution object (aka Assignment) to print it - for location_idx, time_window in enumerate(data['time_windows']): - if location_idx == 0: - continue - index = manager.NodeToIndex(location_idx) - time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1]) - routing.AddToAssignment(time_dimension.SlackVar(index)) - # Add time window constraints for each vehicle start node - # and 'copy' the slack var in the solution object (aka Assignment) to print it - for vehicle_id in range(data['num_vehicles']): - index = routing.Start(vehicle_id) - time_dimension.CumulVar(index).SetRange(data['time_windows'][0][0], - data['time_windows'][0][1]) - routing.AddToAssignment(time_dimension.SlackVar(index)) - # Warning: Slack var is not defined for vehicle's end node - #routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id))) - - -# [START solution_printer] -def print_solution(manager, routing, assignment): # pylint:disable=too-many-locals - """Prints assignment on console""" - print(f'Objective: {assignment.ObjectiveValue()}') - time_dimension = routing.GetDimensionOrDie('Time') - capacity_dimension = routing.GetDimensionOrDie('Capacity') - total_distance = 0 - total_load = 0 - total_time = 0 - for vehicle_id in range(manager.GetNumberOfVehicles()): - index = routing.Start(vehicle_id) - plan_output = f'Route for vehicle {vehicle_id}:\n' - distance = 0 - while not routing.IsEnd(index): - load_var = capacity_dimension.CumulVar(index) - time_var = time_dimension.CumulVar(index) - slack_var = time_dimension.SlackVar(index) - plan_output += ( - f' {manager.IndexToNode(index)} ' - f'Load({assignment.Value(load_var)}) ' - f'Time({assignment.Min(time_var)},{assignment.Max(time_var)}) ' - f'Slack({assignment.Min(slack_var)},{assignment.Max(slack_var)}) ->' - ) - previous_index = index - index = assignment.Value(routing.NextVar(index)) - distance += routing.GetArcCostForVehicle(previous_index, index, - vehicle_id) - load_var = capacity_dimension.CumulVar(index) - time_var = time_dimension.CumulVar(index) - slack_var = time_dimension.SlackVar(index) - plan_output += ( - f' {manager.IndexToNode(index)} ' - f'Load({assignment.Value(load_var)}) ' - f'Time({assignment.Min(time_var)},{assignment.Max(time_var)})\n') - plan_output += f'Distance of the route: {distance}m\n' - plan_output += f'Load of the route: {assignment.Value(load_var)}\n' - plan_output += f'Time of the route: {assignment.Value(time_var)}\n' - print(plan_output) - total_distance += distance - total_load += assignment.Value(load_var) - total_time += assignment.Value(time_var) - print(f'Total Distance of all routes: {total_distance}m') - print(f'Total Load of all routes: {total_load}') - print(f'Total Time of all routes: {total_time}min') - # [END solution_printer] - - -def main(): - """Solve the Capacitated VRP with time windows.""" - # Instantiate the data problem. - # [START data] - data = create_data_model() - # [END data] - - # Create the routing index manager. - # [START index_manager] - manager = pywrapcp.RoutingIndexManager(data['num_locations'], - data['num_vehicles'], data['depot']) - # [END index_manager] - - # Create Routing Model. - # [START routing_model] - routing = pywrapcp.RoutingModel(manager) - # [END routing_model] - - # Define weight of each edge. - # [START transit_callback] - distance_evaluator_index = routing.RegisterTransitCallback( - partial(create_distance_evaluator(data), manager)) - # [END transit_callback] - - # Define cost of each arc. - # [START arc_cost] - routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index) - # [END arc_cost] - - # Add Capacity constraint. - # [START capacity_constraint] - demand_evaluator_index = routing.RegisterUnaryTransitCallback( - partial(create_demand_evaluator(data), manager)) - add_capacity_constraints(routing, data, demand_evaluator_index) - # [END capacity_constraint] - - # Add Time Window constraint. - # [START time_constraint] - time_evaluator_index = routing.RegisterTransitCallback( - partial(create_time_evaluator(data), manager)) - add_time_window_constraints(routing, manager, data, time_evaluator_index) - # [END time_constraint] - - # Setting first solution heuristic (cheapest addition). - # [START parameters] - search_parameters = pywrapcp.DefaultRoutingSearchParameters() - search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) - search_parameters.local_search_metaheuristic = ( - routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH) - search_parameters.time_limit.FromSeconds(2) - search_parameters.log_search = True - # [END parameters] - - # Solve the problem. - # [START solve] - solution = routing.SolveWithParameters(search_parameters) - # [END solve] - - # Print solution on console. - # [START print_solution] - if solution: - print_solution(manager, routing, solution) - else: - print('No solution found!') - # [END print_solution] - - -if __name__ == '__main__': - main() -# [END program] diff --git a/ortools/constraint_solver/samples/vrpgs.py b/ortools/constraint_solver/samples/vrpgs.py deleted file mode 100755 index 83d94d0476..0000000000 --- a/ortools/constraint_solver/samples/vrpgs.py +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2010-2022 Google LLC -# Copyright 2015 Tin Arm Engineering AB -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# [START program] -"""Simple Vehicle Routing Problem (VRP). - -This is a sample using the routing library Python wrapper to solve a VRP -instance. -A description of the problem can be found here: -http://en.wikipedia.org/wiki/Vehicle_routing_problem. - -Distances are in meters. -""" - -# [START import] -import functools -from ortools.constraint_solver import routing_enums_pb2 -from ortools.constraint_solver import pywrapcp -# [END import] - - -# [START data_model] -def create_data_model(): - """Stores the data for the problem.""" - data = {} - # Locations in block unit - locations_ = [ - # fmt: off - (4, 4), # depot - (2, 0), (8, 0), # locations to visit - (0, 1), (1, 1), - (5, 2), (7, 2), - (3, 3), (6, 3), - (5, 5), (8, 5), - (1, 6), (2, 6), - (3, 7), (6, 7), - (0, 8), (7, 8), - # fmt: on - ] - # Compute locations in meters using the block dimension defined as follow - # Manhattan average block: 750ft x 264ft -> 228m x 80m - # here we use: 114m x 80m city block - # src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance' - data["locations"] = [(l[0] * 114, l[1] * 80) for l in locations_] - data["num_locations"] = len(data["locations"]) - data["num_vehicles"] = 4 - data["depot"] = 0 - return data - -# [END data_model] - - -# [START solution_printer] -def print_solution(data, manager, routing, assignment): - """Prints solution on console.""" - print(f"Objective: {assignment.ObjectiveValue()}") - total_distance = 0 - for vehicle_id in range(data["num_vehicles"]): - index = routing.Start(vehicle_id) - plan_output = f"Route for vehicle {vehicle_id}:\n" - route_distance = 0 - while not routing.IsEnd(index): - plan_output += f" {manager.IndexToNode(index)} ->" - previous_index = index - index = assignment.Value(routing.NextVar(index)) - route_distance += routing.GetArcCostForVehicle( - previous_index, index, vehicle_id - ) - plan_output += f" {manager.IndexToNode(index)}\n" - plan_output += f"Distance of the route: {route_distance}m\n" - print(plan_output) - total_distance += route_distance - print(f"Total Distance of all routes: {total_distance}m") - -# [END solution_printer] - - -####################### -# Problem Constraints # -####################### -def manhattan_distance(position_1, position_2): - """Computes the Manhattan distance between two points.""" - return abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1]) - - -def create_distance_evaluator(data): - """Creates callback to return distance between points.""" - distances_ = {} - # precompute distance between location to have distance callback in O(1) - for from_node in range(data["num_locations"]): - distances_[from_node] = {} - for to_node in range(data["num_locations"]): - if from_node == to_node: - distances_[from_node][to_node] = 0 - else: - distances_[from_node][to_node] = manhattan_distance( - data["locations"][from_node], data["locations"][to_node] - ) - - def distance_evaluator(manager, from_index, to_index): - """Returns the manhattan distance between the two nodes.""" - # Convert from routing variable Index to distance matrix NodeIndex. - from_node = manager.IndexToNode(from_index) - to_node = manager.IndexToNode(to_index) - return distances_[from_node][to_node] - - return distance_evaluator - - -def add_distance_dimension(routing, distance_evaluator_index): - """Add Global Span constraint.""" - distance = "Distance" - routing.AddDimension( - distance_evaluator_index, - 0, # null slack - 3000, # maximum distance per vehicle - True, # start cumul to zero - distance, - ) - distance_dimension = routing.GetDimensionOrDie(distance) - # Try to minimize the max distance among vehicles. - # /!\ It doesn't mean the standard deviation is minimized - distance_dimension.SetGlobalSpanCostCoefficient(100) - - -def main(): - """Entry point of the program.""" - # Instantiate the data problem. - # [START data] - data = create_data_model() - # [END data] - - # Create the routing index manager. - # [START index_manager] - manager = pywrapcp.RoutingIndexManager( - data["num_locations"], data["num_vehicles"], data["depot"] - ) - # [END index_manager] - - # Create Routing Model. - # [START routing_model] - routing = pywrapcp.RoutingModel(manager) - # [END routing_model] - - # Define weight of each edge - # [START transit_callback] - distance_evaluator_index = routing.RegisterTransitCallback( - functools.partial(create_distance_evaluator(data), manager) - ) - # [END transit_callback] - - # Define cost of each arc. - # [START arc_cost] - routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index) - # [END arc_cost] - - # Add Distance constraint. - # [START distance_constraint] - add_distance_dimension(routing, distance_evaluator_index) - # [END distance_constraint] - - # Setting first solution heuristic. - # [START parameters] - search_parameters = pywrapcp.DefaultRoutingSearchParameters() - search_parameters.first_solution_strategy = ( - routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC - ) - # [END parameters] - - # Solve the problem. - # [START solve] - solution = routing.SolveWithParameters(search_parameters) - # [END solve] - - # Print solution on console. - # [START print_solution] - if solution: - print_solution(data, manager, routing, solution) - else: - print("No solution found !") - # [END print_solution] - - -if __name__ == "__main__": - main() - # [END program] diff --git a/ortools/dotnet/Google.OrTools-full.csproj.in b/ortools/dotnet/Google.OrTools-full.csproj.in index a7761d115a..e780ea1b1d 100644 --- a/ortools/dotnet/Google.OrTools-full.csproj.in +++ b/ortools/dotnet/Google.OrTools-full.csproj.in @@ -184,7 +184,7 @@ - + diff --git a/ortools/dotnet/Google.OrTools-local.csproj.in b/ortools/dotnet/Google.OrTools-local.csproj.in index cbdc6deada..5d47dd3b8e 100644 --- a/ortools/dotnet/Google.OrTools-local.csproj.in +++ b/ortools/dotnet/Google.OrTools-local.csproj.in @@ -172,7 +172,7 @@ - + diff --git a/ortools/java/pom-full.xml.in b/ortools/java/pom-full.xml.in index d47b03ed1b..3209c3d78c 100644 --- a/ortools/java/pom-full.xml.in +++ b/ortools/java/pom-full.xml.in @@ -109,7 +109,7 @@ com.google.protobuf protobuf-java - 3.24.4 + 3.25.0 diff --git a/ortools/java/pom-local.xml.in b/ortools/java/pom-local.xml.in index 40dfe06d03..916767aae0 100644 --- a/ortools/java/pom-local.xml.in +++ b/ortools/java/pom-local.xml.in @@ -81,7 +81,7 @@ com.google.protobuf protobuf-java - 3.24.4 + 3.25.0 diff --git a/ortools/java/pom.xml.in b/ortools/java/pom.xml.in index 6859a721cf..6d63265bba 100644 --- a/ortools/java/pom.xml.in +++ b/ortools/java/pom.xml.in @@ -69,7 +69,7 @@ com.google.protobuf protobuf-java - 3.24.4 + 3.25.0 junit diff --git a/ortools/python/setup.py.in b/ortools/python/setup.py.in index 1307e4bde6..1acb3690ef 100644 --- a/ortools/python/setup.py.in +++ b/ortools/python/setup.py.in @@ -46,7 +46,7 @@ setup( 'absl-py >= 2.0.0', 'numpy >= 1.13.3', 'pandas >= 2.0.0', - 'protobuf >= 4.24.4', + 'protobuf >= 4.25.0', ], package_data={ '@PYTHON_PROJECT@':[$<$,SHARED_LIBRARY>:'.libs/*','../$'>], diff --git a/ortools/routing/README.md b/ortools/routing/README.md index 0c17d21088..f4af66a9c2 100644 --- a/ortools/routing/README.md +++ b/ortools/routing/README.md @@ -7,15 +7,15 @@ file formats. `solution_serializer.h` contains a generic serializer for routing solutions for many formats. -| Problem type | File format | Corresponding parser | Data sets | -| ------------- | ----------- | -------------------- | ---------------------- | -| TSP | TSPLIB | `tsplib_parser.h` | [TSPLIB95][tsplib95] | -| TSPTW | TSPTW | `tsptw_parser.h` | [TSPTW][tsptw] | -| PDTSP / TSPPD | PDTSP | `pdtsp_parser.h` | [PDTSP][pdtsp] | -| CVRP | TSPLIB | `tsplib_parser.h` | [TSPLIB95][tsplib95] | -| VRPTW | Solomon | `solomon_parser.h` | [Solomon][solomon],

[Homberger][homberger] | -| CARP | CARPLIB | `carplib_parser.h` | [CARPLIB][carplib] | -| NEARP | NEARPLIB | `nearplib_parser.h` | [NEARPLIB][nearplib] | +| Problem type | File format | Corresponding parser | Data sets | +| ------------ | ----------- | -------------------- | --------- | +| TSP | TSPLIB | `tsplib_parser.h` | [TSPLIB95][tsplib95] | +| TSPTW | TSPTW | `tsptw_parser.h` | [TSPTW][tsptw] | +| PDTSP / TSPPD | PDTSP | `pdtsp_parser.h` | [PDTSP][pdtsp] | +| CVRP | TSPLIB | `tsplib_parser.h` | [TSPLIB95][tsplib95] | +| VRPTW | Solomon | `solomon_parser.h` | [Solomon][solomon], [Homberger][homberger] | +| CARP | CARPLIB | `carplib_parser.h` | [CARPLIB][carplib] | +| NEARP | NEARPLIB | `nearplib_parser.h` | [NEARPLIB][nearplib] | In the future, this folder will contain the whole routing solver. diff --git a/patches/BUILD.bazel b/patches/BUILD.bazel index 3d97702158..f514593a1a 100644 --- a/patches/BUILD.bazel +++ b/patches/BUILD.bazel @@ -12,8 +12,8 @@ # limitations under the License. exports_files([ - "abseil-cpp-20230802.0.patch", - "protobuf-v24.4.patch", + "abseil-cpp-20230802.1.patch", + "protobuf-v25.0.patch", "pybind11.patch", "pybind11_bazel.patch", "pybind11_protobuf.patch", diff --git a/patches/abseil-cpp-20230802.0.patch b/patches/abseil-cpp-20230802.1.patch similarity index 100% rename from patches/abseil-cpp-20230802.0.patch rename to patches/abseil-cpp-20230802.1.patch diff --git a/patches/protobuf-v24.4.patch b/patches/protobuf-v25.0.patch similarity index 80% rename from patches/protobuf-v24.4.patch rename to patches/protobuf-v25.0.patch index f22c617375..8e8805fac2 100644 --- a/patches/protobuf-v24.4.patch +++ b/patches/protobuf-v25.0.patch @@ -1,5 +1,5 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index ac0d03c3a..ac8aee8a7 100644 +index f78fccc27..f830c8258 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ else (BUILD_SHARED_LIBS) @@ -69,3 +69,16 @@ index 422754a1a..ea91898d5 100644 target_link_libraries(libprotobuf PUBLIC ${protobuf_ABSL_USED_TARGETS}) protobuf_configure_target(libprotobuf) if(protobuf_BUILD_SHARED_LIBS) +diff --git a/src/google/protobuf/map_field.h b/src/google/protobuf/map_field.h +index 8aa9e45fe..8388f8a2e 100644 +--- a/src/google/protobuf/map_field.h ++++ b/src/google/protobuf/map_field.h +@@ -678,7 +678,7 @@ class MapField final : public TypeDefinedMapFieldBase { + template +-constexpr MapFieldBase::VTable ++PROTOBUF_CONSTINIT const MapFieldBase::VTable + MapField::kVTable = + MapField::template MakeVTable(); + diff --git a/tools/release/build_delivery_meta.sh b/tools/release/build_delivery_meta.sh index 204bb91703..557dca8457 100755 --- a/tools/release/build_delivery_meta.sh +++ b/tools/release/build_delivery_meta.sh @@ -87,7 +87,7 @@ function build_dotnet() { echo -n "Build .Net..." | tee -a build.log cmake -S. -Btemp_dotnet -DBUILD_SAMPLES=OFF -DBUILD_EXAMPLES=OFF \ - -DBUILD_DOTNET=ON -DUNIVERSAL_DOTNET_PACKAGE=ON + -DBUILD_DOTNET=ON -DUSE_DOTNET_462=ON -DUNIVERSAL_DOTNET_PACKAGE=ON cmake --build temp_dotnet -j8 -v echo "DONE" | tee -a build.log #cmake --build temp_dotnet --target test @@ -211,7 +211,7 @@ function main() { local -r RELEASE_DIR="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" echo "RELEASE_DIR: '${RELEASE_DIR}'" | tee -a build.log - (cd "${ROOT_DIR}" && make print-OR_TOOLS_VERSION | tee -a build.log) + #(cd "${ROOT_DIR}" && make print-OR_TOOLS_VERSION | tee -a build.log) local -r ORTOOLS_BRANCH=$(git rev-parse --abbrev-ref HEAD) local -r ORTOOLS_SHA1=$(git rev-parse --verify HEAD) diff --git a/tools/release/build_delivery_win.cmd b/tools/release/build_delivery_win.cmd index 2cb6fa6e2b..2cc1d427f1 100644 --- a/tools/release/build_delivery_win.cmd +++ b/tools/release/build_delivery_win.cmd @@ -128,6 +128,7 @@ rm.exe -rf temp_dotnet echo DONE | tee.exe -a build.log echo Build dotnet: ... | tee.exe -a build.log +set Platform=any cmake -S. -Btemp_dotnet -DBUILD_SAMPLES=OFF -DBUILD_EXAMPLES=OFF -DBUILD_DOTNET=ON -DUSE_DOTNET_462=ON cmake --build temp_dotnet --config Release -j8 -v echo DONE | tee.exe -a build.log