249 lines
9.8 KiB
Plaintext
249 lines
9.8 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "google",
|
|
"metadata": {},
|
|
"source": [
|
|
"##### Copyright 2022 Google LLC."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "apache",
|
|
"metadata": {},
|
|
"source": [
|
|
"Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
|
"you may not use this file except in compliance with the License.\n",
|
|
"You may obtain a copy of the License at\n",
|
|
"\n",
|
|
" http://www.apache.org/licenses/LICENSE-2.0\n",
|
|
"\n",
|
|
"Unless required by applicable law or agreed to in writing, software\n",
|
|
"distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
|
"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
|
"See the License for the specific language governing permissions and\n",
|
|
"limitations under the License.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "basename",
|
|
"metadata": {},
|
|
"source": [
|
|
"# vrp_breaks"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "link",
|
|
"metadata": {},
|
|
"source": [
|
|
"<table align=\"left\">\n",
|
|
"<td>\n",
|
|
"<a href=\"https://colab.research.google.com/github/google/or-tools/blob/master/examples/notebook/constraint_solver/vrp_breaks.ipynb\"><img src=\"https://raw.githubusercontent.com/google/or-tools/master/tools/colab_32px.png\"/>Run in Google Colab</a>\n",
|
|
"</td>\n",
|
|
"<td>\n",
|
|
"<a href=\"https://github.com/google/or-tools/blob/master/ortools/constraint_solver/samples/vrp_breaks.py\"><img src=\"https://raw.githubusercontent.com/google/or-tools/master/tools/github_32px.png\"/>View source on GitHub</a>\n",
|
|
"</td>\n",
|
|
"</table>"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "doc",
|
|
"metadata": {},
|
|
"source": [
|
|
"First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "install",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"!pip install ortools"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "description",
|
|
"metadata": {},
|
|
"source": [
|
|
"Vehicle Routing Problem (VRP) with breaks.\n",
|
|
"\n",
|
|
" This is a sample using the routing library python wrapper to solve a VRP\n",
|
|
" problem.\n",
|
|
" A description of the problem can be found here:\n",
|
|
" http://en.wikipedia.org/wiki/Vehicle_routing_problem.\n",
|
|
"\n",
|
|
" Durations are in minutes.\n",
|
|
"\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "code",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from ortools.constraint_solver import routing_enums_pb2\n",
|
|
"from ortools.constraint_solver import pywrapcp\n",
|
|
"\n",
|
|
"\n",
|
|
"def create_data_model():\n",
|
|
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
|
" data = {}\n",
|
|
" data['num_vehicles'] = 4\n",
|
|
" data['depot'] = 0\n",
|
|
" data['time_matrix'] = [\n",
|
|
" [0, 27, 38, 34, 29, 13, 25, 9, 15, 9, 26, 25, 19, 17, 23, 38, 33],\n",
|
|
" [27, 0, 34, 15, 9, 25, 36, 17, 34, 37, 54, 29, 24, 33, 50, 43, 60],\n",
|
|
" [38, 34, 0, 49, 43, 25, 13, 40, 23, 37, 20, 63, 58, 56, 39, 77, 37],\n",
|
|
" [34, 15, 49, 0, 5, 32, 43, 25, 42, 44, 61, 25, 31, 41, 58, 28, 67],\n",
|
|
" [29, 9, 43, 5, 0, 26, 38, 19, 36, 38, 55, 20, 25, 35, 52, 33, 62],\n",
|
|
" [13, 25, 25, 32, 26, 0, 11, 15, 9, 12, 29, 38, 33, 31, 25, 52, 35],\n",
|
|
" [25, 36, 13, 43, 38, 11, 0, 26, 9, 23, 17, 50, 44, 42, 25, 63, 24],\n",
|
|
" [9, 17, 40, 25, 19, 15, 26, 0, 17, 19, 36, 23, 17, 16, 33, 37, 42],\n",
|
|
" [15, 34, 23, 42, 36, 9, 9, 17, 0, 13, 19, 40, 34, 33, 16, 54, 25],\n",
|
|
" [9, 37, 37, 44, 38, 12, 23, 19, 13, 0, 17, 26, 21, 19, 13, 40, 23],\n",
|
|
" [26, 54, 20, 61, 55, 29, 17, 36, 19, 17, 0, 43, 38, 36, 19, 57, 17],\n",
|
|
" [25, 29, 63, 25, 20, 38, 50, 23, 40, 26, 43, 0, 5, 15, 32, 13, 42],\n",
|
|
" [19, 24, 58, 31, 25, 33, 44, 17, 34, 21, 38, 5, 0, 9, 26, 19, 36],\n",
|
|
" [17, 33, 56, 41, 35, 31, 42, 16, 33, 19, 36, 15, 9, 0, 17, 21, 26],\n",
|
|
" [23, 50, 39, 58, 52, 25, 25, 33, 16, 13, 19, 32, 26, 17, 0, 38, 9],\n",
|
|
" [38, 43, 77, 28, 33, 52, 63, 37, 54, 40, 57, 13, 19, 21, 38, 0, 39],\n",
|
|
" [33, 60, 37, 67, 62, 35, 24, 42, 25, 23, 17, 42, 36, 26, 9, 39, 0],\n",
|
|
" ]\n",
|
|
" # 15 min of service time\n",
|
|
" data['service_time'] = [15] * len(data['time_matrix'])\n",
|
|
" data['service_time'][data['depot']] = 0\n",
|
|
" assert len(data['time_matrix']) == len(data['service_time'])\n",
|
|
" return data\n",
|
|
"\n",
|
|
"\n",
|
|
"def print_solution(manager, routing, solution):\n",
|
|
" \"\"\"Prints solution on console.\"\"\"\n",
|
|
" print(f'Objective: {solution.ObjectiveValue()}')\n",
|
|
"\n",
|
|
" print('Breaks:')\n",
|
|
" intervals = solution.IntervalVarContainer()\n",
|
|
" for i in range(intervals.Size()):\n",
|
|
" brk = intervals.Element(i)\n",
|
|
" if brk.PerformedValue():\n",
|
|
" print(f'{brk.Var().Name()}: ' +\n",
|
|
" f'Start({brk.StartValue()}) Duration({brk.DurationValue()})')\n",
|
|
" else:\n",
|
|
" print(f'{brk.Var().Name()}: Unperformed')\n",
|
|
"\n",
|
|
" time_dimension = routing.GetDimensionOrDie('Time')\n",
|
|
" total_time = 0\n",
|
|
" for vehicle_id in range(manager.GetNumberOfVehicles()):\n",
|
|
" index = routing.Start(vehicle_id)\n",
|
|
" plan_output = f'Route for vehicle {vehicle_id}:\\n'\n",
|
|
" while not routing.IsEnd(index):\n",
|
|
" time_var = time_dimension.CumulVar(index)\n",
|
|
" plan_output += f'{manager.IndexToNode(index)} '\n",
|
|
" plan_output += f'Time({solution.Value(time_var)}) -> '\n",
|
|
" index = solution.Value(routing.NextVar(index))\n",
|
|
" time_var = time_dimension.CumulVar(index)\n",
|
|
" plan_output += f'{manager.IndexToNode(index)} '\n",
|
|
" plan_output += f'Time({solution.Value(time_var)})\\n'\n",
|
|
" plan_output += f'Time of the route: {solution.Value(time_var)}min\\n'\n",
|
|
" print(plan_output)\n",
|
|
" total_time += solution.Value(time_var)\n",
|
|
" print(f'Total time of all routes: {total_time}min')\n",
|
|
"\n",
|
|
"\n",
|
|
"def main():\n",
|
|
" \"\"\"Solve the VRP with time windows.\"\"\"\n",
|
|
" # Instantiate the data problem.\n",
|
|
" data = create_data_model()\n",
|
|
"\n",
|
|
" # Create the routing index manager.\n",
|
|
" manager = pywrapcp.RoutingIndexManager(len(data['time_matrix']),\n",
|
|
" data['num_vehicles'], data['depot'])\n",
|
|
"\n",
|
|
" # Create Routing Model.\n",
|
|
" routing = pywrapcp.RoutingModel(manager)\n",
|
|
"\n",
|
|
"\n",
|
|
" # Create and register a transit callback.\n",
|
|
" def time_callback(from_index, to_index):\n",
|
|
" \"\"\"Returns the travel time + service time between the two nodes.\"\"\"\n",
|
|
" # Convert from routing variable Index to time matrix NodeIndex.\n",
|
|
" from_node = manager.IndexToNode(from_index)\n",
|
|
" to_node = manager.IndexToNode(to_index)\n",
|
|
" return data['time_matrix'][from_node][to_node] + data['service_time'][\n",
|
|
" from_node]\n",
|
|
"\n",
|
|
" transit_callback_index = routing.RegisterTransitCallback(time_callback)\n",
|
|
"\n",
|
|
" # Define cost of each arc.\n",
|
|
" routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)\n",
|
|
"\n",
|
|
" # Add Time Windows constraint.\n",
|
|
" time = 'Time'\n",
|
|
" routing.AddDimension(\n",
|
|
" transit_callback_index,\n",
|
|
" 10, # needed optional waiting time to place break\n",
|
|
" 180, # maximum time per vehicle\n",
|
|
" True, # Force start cumul to zero.\n",
|
|
" time)\n",
|
|
" time_dimension = routing.GetDimensionOrDie(time)\n",
|
|
" time_dimension.SetGlobalSpanCostCoefficient(10)\n",
|
|
"\n",
|
|
" # Breaks\n",
|
|
" # warning: Need a pre-travel array using the solver's index order.\n",
|
|
" node_visit_transit = [0] * routing.Size()\n",
|
|
" for index in range(routing.Size()):\n",
|
|
" node = manager.IndexToNode(index)\n",
|
|
" node_visit_transit[index] = data['service_time'][node]\n",
|
|
"\n",
|
|
" break_intervals = {}\n",
|
|
" for v in range(manager.GetNumberOfVehicles()):\n",
|
|
" break_intervals[v] = [\n",
|
|
" routing.solver().FixedDurationIntervalVar(\n",
|
|
" 50, # start min\n",
|
|
" 60, # start max\n",
|
|
" 10, # duration: 10 min\n",
|
|
" False, # optional: no\n",
|
|
" f'Break for vehicle {v}')\n",
|
|
" ]\n",
|
|
" time_dimension.SetBreakIntervalsOfVehicle(\n",
|
|
" break_intervals[v], # breaks\n",
|
|
" v, # vehicle index\n",
|
|
" node_visit_transit)\n",
|
|
"\n",
|
|
" # Setting first solution heuristic.\n",
|
|
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
|
" search_parameters.first_solution_strategy = (\n",
|
|
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
|
" search_parameters.local_search_metaheuristic = (\n",
|
|
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
|
" # search_parameters.log_search = True\n",
|
|
" search_parameters.time_limit.FromSeconds(2)\n",
|
|
"\n",
|
|
" # Solve the problem.\n",
|
|
" solution = routing.SolveWithParameters(search_parameters)\n",
|
|
"\n",
|
|
" # Print solution on console.\n",
|
|
" if solution:\n",
|
|
" print_solution(manager, routing, solution)\n",
|
|
" else:\n",
|
|
" print('No solution found !')\n",
|
|
"\n",
|
|
"\n",
|
|
"main()\n",
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|