Update Notebooks
This commit is contained in:
@@ -144,12 +144,12 @@
|
||||
" -_capacity,\n",
|
||||
" -_capacity,\n",
|
||||
" -_capacity,\n",
|
||||
" 1, 1, # 1, 2\n",
|
||||
" 2, 4, # 3, 4\n",
|
||||
" 2, 4, # 5, 6\n",
|
||||
" 3, 3, # 1, 2\n",
|
||||
" 3, 4, # 3, 4\n",
|
||||
" 3, 4, # 5, 6\n",
|
||||
" 8, 8, # 7, 8\n",
|
||||
" 1, 2, # 9,10\n",
|
||||
" 1, 2, # 11,12\n",
|
||||
" 3, 3, # 9,10\n",
|
||||
" 3, 3, # 11,12\n",
|
||||
" 4, 4, # 13, 14\n",
|
||||
" 8, 8] # 15, 16\n",
|
||||
" data['time_per_demand_unit'] = 5 # 5 minutes/unit\n",
|
||||
@@ -160,16 +160,18 @@
|
||||
" (0, 1000),\n",
|
||||
" (0, 1000),\n",
|
||||
" (0, 1000),\n",
|
||||
" (75, 8500), (75, 8500), # 1, 2\n",
|
||||
" (60, 7000), (45, 5500), # 3, 4\n",
|
||||
" (0, 8000), (50, 6000), # 5, 6\n",
|
||||
" (0, 1000), (10, 2000), # 7, 8\n",
|
||||
" (0, 1000), (75, 8500), # 9, 10\n",
|
||||
" (85, 9500), (5, 1500), # 11, 12\n",
|
||||
" (15, 2500), (10, 2000), # 13, 14\n",
|
||||
" (45, 5500), (30, 4000)] # 15, 16\n",
|
||||
" (75, 850), (75, 850), # 1, 2\n",
|
||||
" (60, 700), (45, 550), # 3, 4\n",
|
||||
" (0, 800), (50, 600), # 5, 6\n",
|
||||
" (0, 1000), (10, 200), # 7, 8\n",
|
||||
" (0, 1000), (75, 850), # 9, 10\n",
|
||||
" (85, 950), (5, 150), # 11, 12\n",
|
||||
" (15, 250), (10, 200), # 13, 14\n",
|
||||
" (45, 550), (30, 400)] # 15, 16\n",
|
||||
" data['num_vehicles'] = 3\n",
|
||||
" data['vehicle_capacity'] = _capacity\n",
|
||||
" data['vehicle_max_distance'] = 10_000\n",
|
||||
" data['vehicle_max_time'] = 1_500\n",
|
||||
" data[\n",
|
||||
" 'vehicle_speed'] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min\n",
|
||||
" data['depot'] = 0\n",
|
||||
@@ -194,6 +196,9 @@
|
||||
" for to_node in range(data['num_locations']):\n",
|
||||
" if from_node == to_node:\n",
|
||||
" _distances[from_node][to_node] = 0\n",
|
||||
" # Forbid start/end/reload node to be consecutive.\n",
|
||||
" elif from_node in range(6) and to_node in range(6):\n",
|
||||
" _distances[from_node][to_node] = data['vehicle_max_distance']\n",
|
||||
" else:\n",
|
||||
" _distances[from_node][to_node] = (manhattan_distance(\n",
|
||||
" data['locations'][from_node], data['locations'][to_node]))\n",
|
||||
@@ -206,13 +211,13 @@
|
||||
" return distance_evaluator\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def add_distance_dimension(routing, distance_evaluator_index):\n",
|
||||
"def add_distance_dimension(routing, manager, data, distance_evaluator_index):\n",
|
||||
" \"\"\"Add Global Span constraint\"\"\"\n",
|
||||
" distance = 'Distance'\n",
|
||||
" routing.AddDimension(\n",
|
||||
" distance_evaluator_index,\n",
|
||||
" 0, # null slack\n",
|
||||
" 10000, # maximum distance per vehicle\n",
|
||||
" data['vehicle_max_distance'], # maximum distance per vehicle\n",
|
||||
" True, # start cumul to zero\n",
|
||||
" distance)\n",
|
||||
" distance_dimension = routing.GetDimensionOrDie(distance)\n",
|
||||
@@ -238,7 +243,7 @@
|
||||
" capacity = 'Capacity'\n",
|
||||
" routing.AddDimension(\n",
|
||||
" demand_evaluator_index,\n",
|
||||
" 0, # Null slack\n",
|
||||
" vehicle_capacity,\n",
|
||||
" vehicle_capacity,\n",
|
||||
" True, # start cumul to zero\n",
|
||||
" capacity)\n",
|
||||
@@ -247,11 +252,17 @@
|
||||
" # e.g. vehicle with load 10/15 arrives at node 1 (depot unload)\n",
|
||||
" # so we have CumulVar = 10(current load) + -15(unload) + 5(slack) = 0.\n",
|
||||
" capacity_dimension = routing.GetDimensionOrDie(capacity)\n",
|
||||
" # Allow to drop reloading nodes with zero cost.\n",
|
||||
" for node_index in [1, 2, 3, 4, 5]:\n",
|
||||
" index = manager.NodeToIndex(node_index)\n",
|
||||
" capacity_dimension.SlackVar(index).SetRange(0, vehicle_capacity)\n",
|
||||
" routing.AddDisjunction([node_index], 0)\n",
|
||||
"\n",
|
||||
" # Allow to drop regular node with a cost.\n",
|
||||
" for node_index in range(6, len(data['demands'])):\n",
|
||||
" index = manager.NodeToIndex(node_index)\n",
|
||||
" capacity_dimension.SlackVar(index).SetValue(0)\n",
|
||||
" routing.AddDisjunction([node_index], 100_000)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_time_evaluator(data):\n",
|
||||
" \"\"\"Creates callback to get total times between locations.\"\"\"\n",
|
||||
@@ -265,8 +276,8 @@
|
||||
" if from_node == to_node:\n",
|
||||
" travel_time = 0\n",
|
||||
" else:\n",
|
||||
" travel_time = manhattan_distance(data['locations'][\n",
|
||||
" from_node], data['locations'][to_node]) / data['vehicle_speed']\n",
|
||||
" travel_time = manhattan_distance(\n",
|
||||
" data['locations'][from_node], data['locations'][to_node]) / data['vehicle_speed']\n",
|
||||
" return travel_time\n",
|
||||
"\n",
|
||||
" _total_time = {}\n",
|
||||
@@ -292,11 +303,11 @@
|
||||
"def add_time_window_constraints(routing, manager, data, time_evaluator):\n",
|
||||
" \"\"\"Add Time windows constraint\"\"\"\n",
|
||||
" time = 'Time'\n",
|
||||
" horizon = 1500\n",
|
||||
" max_time = data['vehicle_max_time']\n",
|
||||
" routing.AddDimension(\n",
|
||||
" time_evaluator,\n",
|
||||
" horizon, # allow waiting time\n",
|
||||
" horizon, # maximum time per vehicle\n",
|
||||
" max_time, # allow waiting time\n",
|
||||
" max_time, # maximum time per vehicle\n",
|
||||
" False, # don't force start cumul to zero since we are giving TW to start nodes\n",
|
||||
" time)\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(time)\n",
|
||||
@@ -324,22 +335,27 @@
|
||||
"###########\n",
|
||||
"def print_solution(data, manager, routing, assignment): # pylint:disable=too-many-locals\n",
|
||||
" \"\"\"Prints assignment on console\"\"\"\n",
|
||||
" print('Objective: {}'.format(assignment.ObjectiveValue()))\n",
|
||||
" print(f'Objective: {assignment.ObjectiveValue()}')\n",
|
||||
" total_distance = 0\n",
|
||||
" total_load = 0\n",
|
||||
" total_time = 0\n",
|
||||
" capacity_dimension = routing.GetDimensionOrDie('Capacity')\n",
|
||||
" time_dimension = routing.GetDimensionOrDie('Time')\n",
|
||||
" dropped = []\n",
|
||||
" for order in range(0, routing.nodes()):\n",
|
||||
" for order in range(6, routing.nodes()):\n",
|
||||
" index = manager.NodeToIndex(order)\n",
|
||||
" if assignment.Value(routing.NextVar(index)) == index:\n",
|
||||
" dropped.append(order)\n",
|
||||
" print('dropped orders: {}'.format(dropped))\n",
|
||||
" print(f'dropped orders: {dropped}')\n",
|
||||
" for reload in range(1, 6):\n",
|
||||
" index = manager.NodeToIndex(reload)\n",
|
||||
" if assignment.Value(routing.NextVar(index)) == index:\n",
|
||||
" dropped.append(reload)\n",
|
||||
" print(f'dropped reload stations: {dropped}')\n",
|
||||
"\n",
|
||||
" for vehicle_id in range(data['num_vehicles']):\n",
|
||||
" index = routing.Start(vehicle_id)\n",
|
||||
" plan_output = 'Route for vehicle {}:\\n'.format(vehicle_id)\n",
|
||||
" plan_output = f'Route for vehicle {vehicle_id}:\\n'\n",
|
||||
" distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" load_var = capacity_dimension.CumulVar(index)\n",
|
||||
@@ -358,11 +374,9 @@
|
||||
" manager.IndexToNode(index),\n",
|
||||
" assignment.Value(load_var),\n",
|
||||
" assignment.Min(time_var), assignment.Max(time_var))\n",
|
||||
" plan_output += 'Distance of the route: {}m\\n'.format(distance)\n",
|
||||
" plan_output += 'Load of the route: {}\\n'.format(\n",
|
||||
" assignment.Value(load_var))\n",
|
||||
" plan_output += 'Time of the route: {}min\\n'.format(\n",
|
||||
" assignment.Value(time_var))\n",
|
||||
" plan_output += f'Distance of the route: {distance}m\\n'\n",
|
||||
" plan_output += f'Load of the route: {assignment.Value(load_var)}\\n'\n",
|
||||
" plan_output += f'Time of the route: {assignment.Value(time_var)}min\\n'\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += distance\n",
|
||||
" total_load += assignment.Value(load_var)\n",
|
||||
@@ -392,7 +406,7 @@
|
||||
"routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)\n",
|
||||
"\n",
|
||||
"# Add Distance constraint to minimize the longuest route\n",
|
||||
"add_distance_dimension(routing, distance_evaluator_index)\n",
|
||||
"add_distance_dimension(routing, manager, data, distance_evaluator_index)\n",
|
||||
"\n",
|
||||
"# Add Capacity constraint\n",
|
||||
"demand_evaluator_index = routing.RegisterUnaryTransitCallback(\n",
|
||||
@@ -408,9 +422,16 @@
|
||||
"search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
"search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member\n",
|
||||
"search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
"search_parameters.time_limit.FromSeconds(3)\n",
|
||||
"\n",
|
||||
"# Solve the problem.\n",
|
||||
"assignment = routing.SolveWithParameters(search_parameters)\n",
|
||||
"print_solution(data, manager, routing, assignment)\n",
|
||||
"solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
"if solution:\n",
|
||||
" print_solution(data, manager, routing, solution)\n",
|
||||
"else:\n",
|
||||
" print(\"No solution found !\")\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -85,7 +85,6 @@
|
||||
"# [START import]\n",
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"\n",
|
||||
"# [END import]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -188,7 +188,6 @@
|
||||
" max_route_distance = max(route_distance, max_route_distance)\n",
|
||||
" print('Maximum of the route distances: {}m'.format(max_route_distance))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# [END solution_printer]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -84,7 +84,6 @@
|
||||
"\n",
|
||||
"# [START import]\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"\n",
|
||||
"# [END import]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -196,7 +195,6 @@
|
||||
" max_route_distance = max(route_distance, max_route_distance)\n",
|
||||
" print('Maximum of the route distances: {}m'.format(max_route_distance))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# [END solution_printer]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"##### Copyright 2020 Google LLC."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"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",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# vrptw_store_solution_data"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"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/vrptw_store_solution_data.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/vrptw_store_solution_data.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",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install ortools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Copyright 2010-2018 Google LLC\n",
|
||||
"# 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",
|
||||
"# [START program]\n",
|
||||
"\"\"\"VRPTW example that stores routes and cumulative data in an array.\"\"\"\n",
|
||||
"\n",
|
||||
"# [START import]\n",
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"# [END import]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# [START program_part1]\n",
|
||||
"# [START data_model]\n",
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" data['time_matrix'] = [\n",
|
||||
" [0, 6, 9, 8, 7, 3, 6, 2, 3, 2, 6, 6, 4, 4, 5, 9, 7],\n",
|
||||
" [6, 0, 8, 3, 2, 6, 8, 4, 8, 8, 13, 7, 5, 8, 12, 10, 14],\n",
|
||||
" [9, 8, 0, 11, 10, 6, 3, 9, 5, 8, 4, 15, 14, 13, 9, 18, 9],\n",
|
||||
" [8, 3, 11, 0, 1, 7, 10, 6, 10, 10, 14, 6, 7, 9, 14, 6, 16],\n",
|
||||
" [7, 2, 10, 1, 0, 6, 9, 4, 8, 9, 13, 4, 6, 8, 12, 8, 14],\n",
|
||||
" [3, 6, 6, 7, 6, 0, 2, 3, 2, 2, 7, 9, 7, 7, 6, 12, 8],\n",
|
||||
" [6, 8, 3, 10, 9, 2, 0, 6, 2, 5, 4, 12, 10, 10, 6, 15, 5],\n",
|
||||
" [2, 4, 9, 6, 4, 3, 6, 0, 4, 4, 8, 5, 4, 3, 7, 8, 10],\n",
|
||||
" [3, 8, 5, 10, 8, 2, 2, 4, 0, 3, 4, 9, 8, 7, 3, 13, 6],\n",
|
||||
" [2, 8, 8, 10, 9, 2, 5, 4, 3, 0, 4, 6, 5, 4, 3, 9, 5],\n",
|
||||
" [6, 13, 4, 14, 13, 7, 4, 8, 4, 4, 0, 10, 9, 8, 4, 13, 4],\n",
|
||||
" [6, 7, 15, 6, 4, 9, 12, 5, 9, 6, 10, 0, 1, 3, 7, 3, 10],\n",
|
||||
" [4, 5, 14, 7, 6, 7, 10, 4, 8, 5, 9, 1, 0, 2, 6, 4, 8],\n",
|
||||
" [4, 8, 13, 9, 8, 7, 10, 3, 7, 4, 8, 3, 2, 0, 4, 5, 6],\n",
|
||||
" [5, 12, 9, 14, 12, 6, 6, 7, 3, 3, 4, 7, 6, 4, 0, 9, 2],\n",
|
||||
" [9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9],\n",
|
||||
" [7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0],\n",
|
||||
" ]\n",
|
||||
" data['time_windows'] = [\n",
|
||||
" (0, 5), # depot\n",
|
||||
" (7, 12), # 1\n",
|
||||
" (10, 15), # 2\n",
|
||||
" (16, 18), # 3\n",
|
||||
" (10, 13), # 4\n",
|
||||
" (0, 5), # 5\n",
|
||||
" (5, 10), # 6\n",
|
||||
" (0, 4), # 7\n",
|
||||
" (5, 10), # 8\n",
|
||||
" (0, 3), # 9\n",
|
||||
" (10, 16), # 10\n",
|
||||
" (10, 15), # 11\n",
|
||||
" (0, 5), # 12\n",
|
||||
" (5, 10), # 13\n",
|
||||
" (7, 8), # 14\n",
|
||||
" (10, 15), # 15\n",
|
||||
" (11, 15), # 16\n",
|
||||
" ]\n",
|
||||
" data['num_vehicles'] = 4\n",
|
||||
" data['depot'] = 0\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"# [END data_model]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# [START solution_printer]\n",
|
||||
"def print_solution(routes, cumul_data):\n",
|
||||
" \"\"\"Print the solution.\"\"\"\n",
|
||||
" total_time = 0\n",
|
||||
" route_str = ''\n",
|
||||
" for i, route in enumerate(routes):\n",
|
||||
" route_str += 'Route ' + str(i) + ':\\n'\n",
|
||||
" start_time = cumul_data[i][0][0]\n",
|
||||
" end_time = cumul_data[i][0][1]\n",
|
||||
" route_str += ' ' + str(route[0]) + \\\n",
|
||||
" ' Time(' + str(start_time) + ', ' + str(end_time) + ')'\n",
|
||||
" for j in range(1, len(route)):\n",
|
||||
" start_time = cumul_data[i][j][0]\n",
|
||||
" end_time = cumul_data[i][j][1]\n",
|
||||
" route_str += ' -> ' + str(route[j]) + \\\n",
|
||||
" ' Time(' + str(start_time) + ', ' + str(end_time) + ')'\n",
|
||||
" route_str += '\\n Route time: {} min\\n\\n'.format(start_time)\n",
|
||||
" total_time += cumul_data[i][len(route) - 1][0]\n",
|
||||
" route_str += 'Total time: {} min'.format(total_time)\n",
|
||||
" print(route_str)\n",
|
||||
"\n",
|
||||
"# [END solution_printer]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# [START get_routes]\n",
|
||||
"def get_routes(solution, routing, manager):\n",
|
||||
" \"\"\"Get vehicle routes from a solution and store them in an array.\"\"\"\n",
|
||||
" # Get vehicle routes and store them in a two dimensional array whose\n",
|
||||
" # i,j entry is the jth location visited by vehicle i along its route.\n",
|
||||
" routes = []\n",
|
||||
" for route_nbr in range(routing.vehicles()):\n",
|
||||
" index = routing.Start(route_nbr)\n",
|
||||
" route = [manager.IndexToNode(index)]\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" index = solution.Value(routing.NextVar(index))\n",
|
||||
" route.append(manager.IndexToNode(index))\n",
|
||||
" routes.append(route)\n",
|
||||
" return routes\n",
|
||||
"\n",
|
||||
"# [END get_routes]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# [START get_cumulative_data]\n",
|
||||
"def get_cumul_data(solution, routing, dimension):\n",
|
||||
" \"\"\"Get cumulative data from a dimension and store it in an array.\"\"\"\n",
|
||||
" # Returns an array cumul_data whose i,j entry contains the minimum and\n",
|
||||
" # maximum of CumulVar for the dimension at the jth node on route :\n",
|
||||
" # - cumul_data[i][j][0] is the minimum.\n",
|
||||
" # - cumul_data[i][j][1] is the maximum.\n",
|
||||
"\n",
|
||||
" cumul_data = []\n",
|
||||
" for route_nbr in range(routing.vehicles()):\n",
|
||||
" route_data = []\n",
|
||||
" index = routing.Start(route_nbr)\n",
|
||||
" dim_var = dimension.CumulVar(index)\n",
|
||||
" route_data.append([solution.Min(dim_var), solution.Max(dim_var)])\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" index = solution.Value(routing.NextVar(index))\n",
|
||||
" dim_var = dimension.CumulVar(index)\n",
|
||||
" route_data.append([solution.Min(dim_var), solution.Max(dim_var)])\n",
|
||||
" cumul_data.append(route_data)\n",
|
||||
" return cumul_data\n",
|
||||
"\n",
|
||||
"# [END get_cumulative_data]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\"\"\"Solve the VRP with time windows.\"\"\"\n",
|
||||
"# Instantiate the data problem.\n",
|
||||
"# [START data]\n",
|
||||
"data = create_data_model()\n",
|
||||
"# [END data]\n",
|
||||
"\n",
|
||||
"# Create the routing index manager.\n",
|
||||
"# [START index_manager]\n",
|
||||
"manager = pywrapcp.RoutingIndexManager(len(data['time_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
"# [END index_manager]\n",
|
||||
"\n",
|
||||
"# Create Routing Model.\n",
|
||||
"# [START routing_model]\n",
|
||||
"routing = pywrapcp.RoutingModel(manager)\n",
|
||||
"\n",
|
||||
"# [END routing_model]\n",
|
||||
"\n",
|
||||
"# Create and register a transit callback.\n",
|
||||
"# [START transit_callback]\n",
|
||||
"def time_callback(from_index, to_index):\n",
|
||||
" \"\"\"Returns the travel 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]\n",
|
||||
"\n",
|
||||
"transit_callback_index = routing.RegisterTransitCallback(time_callback)\n",
|
||||
"# [END transit_callback]\n",
|
||||
"\n",
|
||||
"# Define cost of each arc.\n",
|
||||
"# [START arc_cost]\n",
|
||||
"routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)\n",
|
||||
"# [END arc_cost]\n",
|
||||
"\n",
|
||||
"# Add Time Windows constraint.\n",
|
||||
"# [START time_windows_constraint]\n",
|
||||
"time = 'Time'\n",
|
||||
"\n",
|
||||
"routing.AddDimension(\n",
|
||||
" transit_callback_index,\n",
|
||||
" 30, # allow waiting time\n",
|
||||
" 30, # maximum time per vehicle\n",
|
||||
" False, # Don't force cumulative time to be 0 at start of routes.\n",
|
||||
" time)\n",
|
||||
"time_dimension = routing.GetDimensionOrDie(time)\n",
|
||||
"# Add time window constraints for each location except depot.\n",
|
||||
"for location_idx, time_window in enumerate(data['time_windows']):\n",
|
||||
" if location_idx == 0:\n",
|
||||
" continue\n",
|
||||
" index = manager.NodeToIndex(location_idx)\n",
|
||||
" time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])\n",
|
||||
"# Add time window constraints for each vehicle start node.\n",
|
||||
"for vehicle_id in range(data['num_vehicles']):\n",
|
||||
" index = routing.Start(vehicle_id)\n",
|
||||
" time_dimension.CumulVar(index).SetRange(data['time_windows'][0][0],\n",
|
||||
" data['time_windows'][0][1])\n",
|
||||
"# [END time_windows_constraint]\n",
|
||||
"\n",
|
||||
"# Instantiate route start and end times to produce feasible times.\n",
|
||||
"# [START depot_start_end_times]\n",
|
||||
"for i in range(data['num_vehicles']):\n",
|
||||
" routing.AddVariableMinimizedByFinalizer(\n",
|
||||
" time_dimension.CumulVar(routing.Start(i)))\n",
|
||||
" routing.AddVariableMinimizedByFinalizer(\n",
|
||||
" time_dimension.CumulVar(routing.End(i)))\n",
|
||||
"# [END depot_start_end_times]\n",
|
||||
"\n",
|
||||
"# Setting first solution heuristic.\n",
|
||||
"# [START parameters]\n",
|
||||
"search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
"search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
"# [END parameters]\n",
|
||||
"\n",
|
||||
"# Solve the problem.\n",
|
||||
"# [START solve]\n",
|
||||
"solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
"# [END solve]\n",
|
||||
"\n",
|
||||
"# Print solution.\n",
|
||||
"# [START print_solution]\n",
|
||||
"if solution:\n",
|
||||
" routes = get_routes(solution, routing, manager)\n",
|
||||
" cumul_data = get_cumul_data(solution, routing, time_dimension)\n",
|
||||
" print_solution(routes, cumul_data)\n",
|
||||
"# [END print_solution]\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -79,22 +79,26 @@
|
||||
"# 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",
|
||||
"\"\"\"Generates possible daily schedules for workers.\"\"\"\n",
|
||||
"\"\"\"Appointment selection.\n",
|
||||
"\n",
|
||||
"This module maximizes the number of appointments that can\n",
|
||||
"be fulfilled by a crew of installers while staying close to ideal\n",
|
||||
"ratio of appointment types.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"import argparse\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"# overloaded sum() clashes with pytype.\n",
|
||||
"# pytype: disable=wrong-arg-types\n",
|
||||
"\n",
|
||||
"from absl import app\n",
|
||||
"from absl import flags\n",
|
||||
"from ortools.linear_solver import pywraplp\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"PARSER = argparse.ArgumentParser()\n",
|
||||
"PARSER.add_argument(\n",
|
||||
" '--load_min', default=480, type=int, help='Minimum load in minutes')\n",
|
||||
"PARSER.add_argument(\n",
|
||||
" '--load_max', default=540, type=int, help='Maximum load in minutes')\n",
|
||||
"PARSER.add_argument(\n",
|
||||
" '--commute_time', default=30, type=int, help='Commute time in minutes')\n",
|
||||
"PARSER.add_argument(\n",
|
||||
" '--num_workers', default=98, type=int, help='Maximum number of workers.')\n",
|
||||
"FLAGS = flags.FLAGS\n",
|
||||
"flags.DEFINE_integer('load_min', 480, 'Minimum load in minutes.')\n",
|
||||
"flags.DEFINE_integer('load_max', 540, 'Maximum load in minutes.')\n",
|
||||
"flags.DEFINE_integer('commute_time', 30, 'Commute time in minutes.')\n",
|
||||
"flags.DEFINE_integer('num_workers', 98, 'Maximum number of workers.')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class AllSolutionCollector(cp_model.CpSolverSolutionCallback):\n",
|
||||
@@ -115,29 +119,26 @@
|
||||
" return self.__collect\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def find_combinations(durations, load_min, load_max, commute_time):\n",
|
||||
" \"\"\"This methods find all valid combinations of appointments.\n",
|
||||
"def EnumerateAllKnapsacksWithRepetition(item_sizes, total_size_min,\n",
|
||||
" total_size_max):\n",
|
||||
" \"\"\"Enumerate all possible knapsacks with total size in the given range.\n",
|
||||
"\n",
|
||||
" This methods find all combinations of appointments such that the sum of\n",
|
||||
" durations + commute times is between load_min and load_max.\n",
|
||||
" Args:\n",
|
||||
" item_sizes: a list of integers. item_sizes[i] is the size of item #i.\n",
|
||||
" total_size_min: an integer, the minimum total size.\n",
|
||||
" total_size_max: an integer, the maximum total size.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" durations: The durations of all appointments.\n",
|
||||
" load_min: The min number of worked minutes for a valid selection.\n",
|
||||
" load_max: The max number of worked minutes for a valid selection.\n",
|
||||
" commute_time: The commute time between two appointments in minutes.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" A matrix where each line is a valid combinations of appointments.\n",
|
||||
" \"\"\"\n",
|
||||
" Returns:\n",
|
||||
" The list of all the knapsacks whose total size is in the given inclusive\n",
|
||||
" range. Each knapsack is a list [#item0, #item1, ... ], where #itemK is an\n",
|
||||
" nonnegative integer: the number of times we put item #K in the knapsack.\n",
|
||||
" \"\"\"\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
" variables = [\n",
|
||||
" model.NewIntVar(0, load_max // (duration + commute_time), '')\n",
|
||||
" for duration in durations\n",
|
||||
" model.NewIntVar(0, total_size_max // size, '') for size in item_sizes\n",
|
||||
" ]\n",
|
||||
" terms = sum(variables[i] * (duration + commute_time)\n",
|
||||
" for i, duration in enumerate(durations))\n",
|
||||
" model.AddLinearConstraint(terms, load_min, load_max)\n",
|
||||
" load = sum(variables[i] * size for i, size in enumerate(item_sizes))\n",
|
||||
" model.AddLinearConstraint(load, total_size_min, total_size_max)\n",
|
||||
"\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" solution_collector = AllSolutionCollector(variables)\n",
|
||||
@@ -145,86 +146,162 @@
|
||||
" return solution_collector.combinations()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def select(combinations, loads, max_number_of_workers):\n",
|
||||
" \"\"\"This method selects the optimal combination of appointments.\n",
|
||||
"def AggregateItemCollectionsOptimally(item_collections, max_num_collections,\n",
|
||||
" ideal_item_ratios):\n",
|
||||
" \"\"\"Selects a set (with repetition) of combination of items optimally.\n",
|
||||
"\n",
|
||||
" This method uses Mixed Integer Programming to select the optimal mix of\n",
|
||||
" appointments.\n",
|
||||
" Given a set of collections of N possible items (in each collection, an item\n",
|
||||
" may appear multiple times), a given \"ideal breakdown of items\", and a\n",
|
||||
" maximum number of collections, this method finds the optimal way to\n",
|
||||
" aggregate the collections in order to:\n",
|
||||
" - maximize the overall number of items\n",
|
||||
" - while keeping the ratio of each item, among the overall selection, as close\n",
|
||||
" as possible to a given input ratio (which depends on the item).\n",
|
||||
" Each collection may be selected more than one time.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" item_collections: a list of item collections. Each item collection is a\n",
|
||||
" list of integers [#item0, ..., #itemN-1], where #itemK is the number\n",
|
||||
" of times item #K appears in the collection, and N is the number of\n",
|
||||
" distinct items.\n",
|
||||
" max_num_collections: an integer, the maximum number of item collections\n",
|
||||
" that may be selected (counting repetitions of the same collection).\n",
|
||||
" ideal_item_ratios: A list of N float which sums to 1.0: the K-th element is\n",
|
||||
" the ideal ratio of item #K in the whole aggregated selection.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" A pair (objective value, list of pairs (item collection, num_selections)),\n",
|
||||
" where:\n",
|
||||
" - \"objective value\" is the value of the internal objective function used\n",
|
||||
" by the MIP Solver\n",
|
||||
" - Each \"item collection\" is an element of the input item_collections\n",
|
||||
" - and its associated \"num_selections\" is the number of times it was\n",
|
||||
" selected.\n",
|
||||
" \"\"\"\n",
|
||||
" solver = pywraplp.Solver('Select',\n",
|
||||
" pywraplp.Solver.CBC_MIXED_INTEGER_PROGRAMMING)\n",
|
||||
" num_vars = len(loads)\n",
|
||||
" num_combinations = len(combinations)\n",
|
||||
" variables = [\n",
|
||||
" solver.IntVar(0, max_number_of_workers, 's[%d]' % i)\n",
|
||||
" for i in range(num_combinations)\n",
|
||||
" ]\n",
|
||||
" achieved = [\n",
|
||||
" solver.IntVar(0, 1000, 'achieved[%d]' % i) for i in range(num_vars)\n",
|
||||
" ]\n",
|
||||
" transposed = [[\n",
|
||||
" combinations[type][index] for type in range(num_combinations)\n",
|
||||
" ] for index in range(num_vars)]\n",
|
||||
" pywraplp.Solver.SCIP_MIXED_INTEGER_PROGRAMMING)\n",
|
||||
" n = len(ideal_item_ratios)\n",
|
||||
" num_distinct_collections = len(item_collections)\n",
|
||||
" max_num_items_per_collection = 0\n",
|
||||
" for template in item_collections:\n",
|
||||
" max_num_items_per_collection = max(max_num_items_per_collection,\n",
|
||||
" sum(template))\n",
|
||||
" upper_bound = max_num_items_per_collection * max_num_collections\n",
|
||||
"\n",
|
||||
" # Maintain the achieved variables.\n",
|
||||
" for i, coefs in enumerate(transposed):\n",
|
||||
" # num_selections_of_collection[i] is an IntVar that represents the number\n",
|
||||
" # of times that we will use collection #i in our global selection.\n",
|
||||
" num_selections_of_collection = [\n",
|
||||
" solver.IntVar(0, max_num_collections, 's[%d]' % i)\n",
|
||||
" for i in range(num_distinct_collections)\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" # num_overall_item[i] is an IntVar that represents the total count of item #i,\n",
|
||||
" # aggregated over all selected collections. This is enforced with dedicated\n",
|
||||
" # constraints that bind them with the num_selections_of_collection vars.\n",
|
||||
" num_overall_item = [\n",
|
||||
" solver.IntVar(0, upper_bound, 'num_overall_item[%d]' % i)\n",
|
||||
" for i in range(n)\n",
|
||||
" ]\n",
|
||||
" for i in range(n):\n",
|
||||
" ct = solver.Constraint(0.0, 0.0)\n",
|
||||
" ct.SetCoefficient(achieved[i], -1)\n",
|
||||
" for j, coef in enumerate(coefs):\n",
|
||||
" ct.SetCoefficient(variables[j], coef)\n",
|
||||
" ct.SetCoefficient(num_overall_item[i], -1)\n",
|
||||
" for j in range(num_distinct_collections):\n",
|
||||
" ct.SetCoefficient(num_selections_of_collection[j],\n",
|
||||
" item_collections[j][i])\n",
|
||||
"\n",
|
||||
" # Simple bound.\n",
|
||||
" solver.Add(solver.Sum(variables) <= max_number_of_workers)\n",
|
||||
" # Maintain the num_all_item variable as the sum of all num_overall_item\n",
|
||||
" # variables.\n",
|
||||
" num_all_items = solver.IntVar(0, upper_bound, 'num_all_items')\n",
|
||||
" solver.Add(solver.Sum(num_overall_item) == num_all_items)\n",
|
||||
"\n",
|
||||
" obj_vars = [\n",
|
||||
" solver.IntVar(0, 1000, 'obj_vars[%d]' % i) for i in range(num_vars)\n",
|
||||
" # Sets the total number of workers.\n",
|
||||
" solver.Add(solver.Sum(num_selections_of_collection) == max_num_collections)\n",
|
||||
"\n",
|
||||
" # Objective variables.\n",
|
||||
" deviation_vars = [\n",
|
||||
" solver.NumVar(0, upper_bound, 'deviation_vars[%d]' % i)\n",
|
||||
" for i in range(n)\n",
|
||||
" ]\n",
|
||||
" for i in range(num_vars):\n",
|
||||
" solver.Add(obj_vars[i] >= achieved[i] - loads[i])\n",
|
||||
" solver.Add(obj_vars[i] >= loads[i] - achieved[i])\n",
|
||||
" for i in range(n):\n",
|
||||
" deviation = deviation_vars[i]\n",
|
||||
" solver.Add(deviation >= num_overall_item[i] -\n",
|
||||
" ideal_item_ratios[i] * num_all_items)\n",
|
||||
" solver.Add(deviation >= ideal_item_ratios[i] * num_all_items -\n",
|
||||
" num_overall_item[i])\n",
|
||||
"\n",
|
||||
" solver.Minimize(solver.Sum(obj_vars))\n",
|
||||
" solver.Maximize(num_all_items - solver.Sum(deviation_vars))\n",
|
||||
"\n",
|
||||
" result_status = solver.Solve()\n",
|
||||
"\n",
|
||||
" # The problem has an optimal solution.\n",
|
||||
" if result_status == pywraplp.Solver.OPTIMAL:\n",
|
||||
" print('Problem solved in %f milliseconds' % solver.WallTime())\n",
|
||||
" return solver.Objective().Value(), [\n",
|
||||
" int(v.SolutionValue()) for v in variables\n",
|
||||
" ]\n",
|
||||
" return -1, []\n",
|
||||
" # The problem has an optimal solution.\n",
|
||||
" return [int(v.solution_value()) for v in num_selections_of_collection]\n",
|
||||
" return []\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_optimal_schedule(demand, args):\n",
|
||||
" \"\"\"Computes the optimal schedule for the appointment selection problem.\"\"\"\n",
|
||||
" combinations = find_combinations([a[2] for a in demand], args.load_min,\n",
|
||||
" args.load_max, args.commute_time)\n",
|
||||
" print('found %d possible combinations of appointements' % len(combinations))\n",
|
||||
"def GetOptimalSchedule(demand):\n",
|
||||
" \"\"\"Computes the optimal schedule for the installation input.\n",
|
||||
"\n",
|
||||
" cost, selection = select(combinations, [a[0]\n",
|
||||
" for a in demand], args.num_workers)\n",
|
||||
" output = [(selection[i], [(combinations[i][t], demand[t][1])\n",
|
||||
" for t in range(len(demand))\n",
|
||||
" if combinations[i][t] != 0])\n",
|
||||
" for i in range(len(selection)) if selection[i] != 0]\n",
|
||||
" return cost, output\n",
|
||||
" Args:\n",
|
||||
" demand: a list of \"appointment types\". Each \"appointment type\" is\n",
|
||||
" a triple (ideal_ratio_pct, name, duration_minutes), where\n",
|
||||
" ideal_ratio_pct is the ideal percentage (in [0..100.0]) of that\n",
|
||||
" type of appointment among all appointments scheduled.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" The same output type as EnumerateAllKnapsacksWithRepetition.\n",
|
||||
" \"\"\"\n",
|
||||
" combinations = EnumerateAllKnapsacksWithRepetition(\n",
|
||||
" [a[2] + FLAGS.commute_time for a in demand], FLAGS.load_min,\n",
|
||||
" FLAGS.load_max)\n",
|
||||
" print(('Found %d possible day schedules ' % len(combinations) +\n",
|
||||
" '(i.e. combination of appointments filling up one worker\\'s day)'))\n",
|
||||
"\n",
|
||||
" selection = AggregateItemCollectionsOptimally(\n",
|
||||
" combinations, FLAGS.num_workers, [a[0] / 100.0 for a in demand])\n",
|
||||
" output = []\n",
|
||||
" for i in range(len(selection)):\n",
|
||||
" if selection[i] != 0:\n",
|
||||
" output.append((selection[i], [(combinations[i][t], demand[t][1])\n",
|
||||
" for t in range(len(demand))\n",
|
||||
" if combinations[i][t] != 0]))\n",
|
||||
"\n",
|
||||
" return output\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\"\"\"Solve the assignment problem.\"\"\"\n",
|
||||
"demand = [(40, 'A1', 90), (30, 'A2', 120), (25, 'A3', 180)]\n",
|
||||
"print('appointments: ')\n",
|
||||
"demand = [(45.0, 'Type1', 90), (30.0, 'Type2', 120), (25.0, 'Type3', 180)]\n",
|
||||
"print('*** input problem ***')\n",
|
||||
"print('Appointments: ')\n",
|
||||
"for a in demand:\n",
|
||||
" print(' %d * %s : %d min' % (a[0], a[1], a[2]))\n",
|
||||
"print('commute time = %d' % args.commute_time)\n",
|
||||
"print('accepted total duration = [%d..%d]' % (args.load_min, args.load_max))\n",
|
||||
"print('%d workers' % args.num_workers)\n",
|
||||
"cost, selection = get_optimal_schedule(demand, args)\n",
|
||||
"print('Optimal solution as a cost of %d' % cost)\n",
|
||||
" print(' %.2f%% of %s : %d min' % (a[0], a[1], a[2]))\n",
|
||||
"print('Commute time = %d' % FLAGS.commute_time)\n",
|
||||
"print('Acceptable duration of a work day = [%d..%d]' %\n",
|
||||
" (FLAGS.load_min, FLAGS.load_max))\n",
|
||||
"print('%d workers' % FLAGS.num_workers)\n",
|
||||
"selection = GetOptimalSchedule(demand)\n",
|
||||
"print()\n",
|
||||
"installed = 0\n",
|
||||
"installed_per_type = {}\n",
|
||||
"for a in demand:\n",
|
||||
" installed_per_type[a[1]] = 0\n",
|
||||
"\n",
|
||||
"print('*** output solution ***')\n",
|
||||
"for template in selection:\n",
|
||||
" print('%d schedules with ' % template[0])\n",
|
||||
" num_instances = template[0]\n",
|
||||
" print('%d schedules with ' % num_instances)\n",
|
||||
" for t in template[1]:\n",
|
||||
" print(' %d installation of type %s' % (t[0], t[1]))\n",
|
||||
" mult = t[0]\n",
|
||||
" print(' %d installation of type %s' % (mult, t[1]))\n",
|
||||
" installed += num_instances * mult\n",
|
||||
" installed_per_type[t[1]] += num_instances * mult\n",
|
||||
"\n",
|
||||
"print()\n",
|
||||
"print('%d installations planned' % installed)\n",
|
||||
"for a in demand:\n",
|
||||
" name = a[1]\n",
|
||||
" per_type = installed_per_type[name]\n",
|
||||
" print((' %d (%.2f%%) installations of type %s planned' %\n",
|
||||
" (per_type, per_type * 100.0 / installed, name)))\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Solves a flexible jobshop problems with the CP-SAT solver.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"import collections\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
@@ -103,10 +102,23 @@
|
||||
"def flexible_jobshop():\n",
|
||||
" \"\"\"Solve a small flexible jobshop problem.\"\"\"\n",
|
||||
" # Data part.\n",
|
||||
" jobs = [[[(3, 0), (1, 1), (5, 2)], [(2, 0), (4, 1), (6, 2)], [(2, 0), (3, 1), (1, 2)]],\n",
|
||||
" [[(2, 0), (3, 1), (4, 2)], [(1, 0), (5, 1), (4, 2)], [(2, 0), (1, 1), (4, 2)]],\n",
|
||||
" [[(2, 0), (1, 1), (4, 2)], [(2, 0), (3, 1), (4, 2)], [(3, 0), (1, 1), (5, 2)]]\n",
|
||||
" ] # yapf:disable\n",
|
||||
" jobs = [ # task = (processing_time, machine_id)\n",
|
||||
" [ # Job 0\n",
|
||||
" [(3, 0), (1, 1), (5, 2)], # alternative 0\n",
|
||||
" [(2, 0), (4, 1), (6, 2)], # alternative 1\n",
|
||||
" [(2, 0), (3, 1), (1, 2)], # alternative 2\n",
|
||||
" ],\n",
|
||||
" [ # Job 1\n",
|
||||
" [(2, 0), (3, 1), (4, 2)],\n",
|
||||
" [(1, 0), (5, 1), (4, 2)],\n",
|
||||
" [(2, 0), (1, 1), (4, 2)],\n",
|
||||
" ],\n",
|
||||
" [ # Job 2\n",
|
||||
" [(2, 0), (1, 1), (4, 2)],\n",
|
||||
" [(2, 0), (3, 1), (4, 2)],\n",
|
||||
" [(3, 0), (1, 1), (5, 2)],\n",
|
||||
" ],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" num_jobs = len(jobs)\n",
|
||||
" all_jobs = range(num_jobs)\n",
|
||||
|
||||
@@ -85,22 +85,39 @@
|
||||
"We have two parallel machines that can perform this job.\n",
|
||||
"One machine can only perform one job at a time.\n",
|
||||
"At any point in time, the sum of the width of the two active jobs does not\n",
|
||||
"exceed a max_length.\n",
|
||||
"exceed a max_width.\n",
|
||||
"\n",
|
||||
"The objective is to minimize the max end time of all jobs.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"from absl import app\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import visualization\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\"\"\"Solves the gate scheduling problem.\"\"\"\n",
|
||||
"model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
"jobs = [[3, 3], [2, 5], [1, 3], [3, 7], [7, 3], [2, 2], [2, 2], [5, 5],\n",
|
||||
" [10, 2], [4, 3], [2, 6], [1, 2], [6, 8], [4, 5], [3, 7]]\n",
|
||||
"jobs = [\n",
|
||||
" [3, 3], # [duration, width]\n",
|
||||
" [2, 5],\n",
|
||||
" [1, 3],\n",
|
||||
" [3, 7],\n",
|
||||
" [7, 3],\n",
|
||||
" [2, 2],\n",
|
||||
" [2, 2],\n",
|
||||
" [5, 5],\n",
|
||||
" [10, 2],\n",
|
||||
" [4, 3],\n",
|
||||
" [2, 6],\n",
|
||||
" [1, 2],\n",
|
||||
" [6, 8],\n",
|
||||
" [4, 5],\n",
|
||||
" [3, 7]\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"max_length = 10\n",
|
||||
"max_width = 10\n",
|
||||
"\n",
|
||||
"horizon = sum(t[0] for t in jobs)\n",
|
||||
"num_jobs = len(jobs)\n",
|
||||
@@ -125,14 +142,14 @@
|
||||
" ends.append(end)\n",
|
||||
" demands.append(jobs[i][1])\n",
|
||||
"\n",
|
||||
" # Create an optional copy of interval to be executed on machine 0.\n",
|
||||
" performed_on_m0 = model.NewBoolVar('perform_%i_on_m0' % i)\n",
|
||||
" performed.append(performed_on_m0)\n",
|
||||
"\n",
|
||||
" # Create an optional copy of interval to be executed on machine 0.\n",
|
||||
" start0 = model.NewIntVar(0, horizon, 'start_%i_on_m0' % i)\n",
|
||||
" end0 = model.NewIntVar(0, horizon, 'end_%i_on_m0' % i)\n",
|
||||
" interval0 = model.NewOptionalIntervalVar(\n",
|
||||
" start0, duration, end0, performed_on_m0, 'interval_%i_on_m0' % i)\n",
|
||||
" interval0 = model.NewOptionalIntervalVar(start0, duration, end0,\n",
|
||||
" performed_on_m0,\n",
|
||||
" 'interval_%i_on_m0' % i)\n",
|
||||
" intervals0.append(interval0)\n",
|
||||
"\n",
|
||||
" # Create an optional copy of interval to be executed on machine 1.\n",
|
||||
@@ -147,8 +164,8 @@
|
||||
" model.Add(start0 == start).OnlyEnforceIf(performed_on_m0)\n",
|
||||
" model.Add(start1 == start).OnlyEnforceIf(performed_on_m0.Not())\n",
|
||||
"\n",
|
||||
"# Max Length constraint (modeled as a cumulative)\n",
|
||||
"model.AddCumulative(intervals, demands, max_length)\n",
|
||||
"# Width constraint (modeled as a cumulative)\n",
|
||||
"model.AddCumulative(intervals, demands, max_width)\n",
|
||||
"\n",
|
||||
"# Choose which machine to perform the jobs on.\n",
|
||||
"model.AddNoOverlap(intervals0)\n",
|
||||
@@ -168,7 +185,7 @@
|
||||
"\n",
|
||||
"# Output solution.\n",
|
||||
"if visualization.RunFromIPython():\n",
|
||||
" output = visualization.SvgWrapper(solver.ObjectiveValue(), max_length,\n",
|
||||
" output = visualization.SvgWrapper(solver.ObjectiveValue(), max_width,\n",
|
||||
" 40.0)\n",
|
||||
" output.AddTitle('Makespan = %i' % solver.ObjectiveValue())\n",
|
||||
" color_manager = visualization.ColorManager()\n",
|
||||
@@ -179,7 +196,7 @@
|
||||
" start = solver.Value(starts[i])\n",
|
||||
" d_x = jobs[i][0]\n",
|
||||
" d_y = jobs[i][1]\n",
|
||||
" s_y = performed_machine * (max_length - d_y)\n",
|
||||
" s_y = performed_machine * (max_width - d_y)\n",
|
||||
" output.AddRectangle(start, s_y, d_x, d_y,\n",
|
||||
" color_manager.RandomColor(), 'black', 'j%i' % i)\n",
|
||||
"\n",
|
||||
@@ -192,8 +209,8 @@
|
||||
" for i in all_jobs:\n",
|
||||
" performed_machine = 1 - solver.Value(performed[i])\n",
|
||||
" start = solver.Value(starts[i])\n",
|
||||
" print(' - Job %i starts at %i on machine %i' % (i, start,\n",
|
||||
" performed_machine))\n",
|
||||
" print(' - Job %i starts at %i on machine %i' %\n",
|
||||
" (i, start, performed_machine))\n",
|
||||
" print('Statistics')\n",
|
||||
" print(' - conflicts : %i' % solver.NumConflicts())\n",
|
||||
" print(' - branches : %i' % solver.NumBranches())\n",
|
||||
|
||||
@@ -115,13 +115,13 @@
|
||||
"\n",
|
||||
"# We expand the creation of the diff array to avoid a pylint warning.\n",
|
||||
"diffs = []\n",
|
||||
"for i in range(0, size - 1):\n",
|
||||
"for i in range(size - 1):\n",
|
||||
" for j in range(i + 1, size):\n",
|
||||
" diffs.append(marks[j] - marks[i])\n",
|
||||
"solver.Add(solver.AllDifferent(diffs))\n",
|
||||
"\n",
|
||||
"solver.Add(marks[size - 1] - marks[size - 2] > marks[1] - marks[0])\n",
|
||||
"for i in range(0, size - 2):\n",
|
||||
"for i in range(size - 2):\n",
|
||||
" solver.Add(marks[i + 1] > marks[i])\n",
|
||||
"\n",
|
||||
"solution = solver.Assignment()\n",
|
||||
|
||||
@@ -81,12 +81,24 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Jobshop with maintenance tasks using the CP-SAT solver.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"import collections\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class SolutionPrinter(cp_model.CpSolverSolutionCallback):\n",
|
||||
" \"\"\"Print intermediate solutions.\"\"\"\n",
|
||||
"\n",
|
||||
" def __init__(self):\n",
|
||||
" cp_model.CpSolverSolutionCallback.__init__(self)\n",
|
||||
" self.__solution_count = 0\n",
|
||||
"\n",
|
||||
" def on_solution_callback(self):\n",
|
||||
" \"\"\"Called at each new solution.\"\"\"\n",
|
||||
" print('Solution %i, time = %f s, objective = %i' %\n",
|
||||
" (self.__solution_count, self.WallTime(), self.ObjectiveValue()))\n",
|
||||
" self.__solution_count += 1\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def jobshop_with_maintenance():\n",
|
||||
" \"\"\"Solves a jobshop with maintenance on one machine.\"\"\"\n",
|
||||
" # Create the model.\n",
|
||||
@@ -95,7 +107,7 @@
|
||||
" jobs_data = [ # task = (machine_id, processing_time).\n",
|
||||
" [(0, 3), (1, 2), (2, 2)], # Job0\n",
|
||||
" [(0, 2), (2, 1), (1, 4)], # Job1\n",
|
||||
" [(1, 4), (2, 3)] # Job2\n",
|
||||
" [(1, 4), (2, 3)], # Job2\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" machines_count = 1 + max(task[0] for job in jobs_data for task in job)\n",
|
||||
@@ -123,8 +135,9 @@
|
||||
" end_var = model.NewIntVar(0, horizon, 'end' + suffix)\n",
|
||||
" interval_var = model.NewIntervalVar(start_var, duration, end_var,\n",
|
||||
" 'interval' + suffix)\n",
|
||||
" all_tasks[job_id, task_id] = task_type(\n",
|
||||
" start=start_var, end=end_var, interval=interval_var)\n",
|
||||
" all_tasks[job_id, task_id] = task_type(start=start_var,\n",
|
||||
" end=end_var,\n",
|
||||
" interval=interval_var)\n",
|
||||
" machine_to_intervals[machine].append(interval_var)\n",
|
||||
"\n",
|
||||
" # Add maintenance interval (machine 0 is not available on time {4, 5, 6, 7}).\n",
|
||||
@@ -150,7 +163,9 @@
|
||||
"\n",
|
||||
" # Solve model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" status = solver.Solve(model)\n",
|
||||
" solution_printer = SolutionPrinter()\n",
|
||||
" status = solver.SolveWithSolutionCallback(model, solution_printer)\n",
|
||||
" #status = solver.Solve(model)\n",
|
||||
"\n",
|
||||
" # Output solution.\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
@@ -160,11 +175,11 @@
|
||||
" for task_id, task in enumerate(job):\n",
|
||||
" machine = task[0]\n",
|
||||
" assigned_jobs[machine].append(\n",
|
||||
" assigned_task_type(\n",
|
||||
" start=solver.Value(all_tasks[job_id, task_id].start),\n",
|
||||
" job=job_id,\n",
|
||||
" index=task_id,\n",
|
||||
" duration=task[1]))\n",
|
||||
" assigned_task_type(start=solver.Value(\n",
|
||||
" all_tasks[job_id, task_id].start),\n",
|
||||
" job=job_id,\n",
|
||||
" index=task_id,\n",
|
||||
" duration=task[1]))\n",
|
||||
"\n",
|
||||
" # Create per machine output lines.\n",
|
||||
" output = ''\n",
|
||||
@@ -193,6 +208,10 @@
|
||||
" # Finally print the solution found.\n",
|
||||
" print('Optimal Schedule Length: %i' % solver.ObjectiveValue())\n",
|
||||
" print(output)\n",
|
||||
" print('Statistics')\n",
|
||||
" print(' - conflicts : %i' % solver.NumConflicts())\n",
|
||||
" print(' - branches : %i' % solver.NumBranches())\n",
|
||||
" print(' - wall time : %f s' % solver.WallTime())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"jobshop_with_maintenance()\n",
|
||||
|
||||
@@ -81,17 +81,18 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Creates a shift scheduling problem and solves it.\"\"\"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"from google.protobuf import text_format\n",
|
||||
"from absl import app\n",
|
||||
"from absl import flags\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"from google.protobuf import text_format\n",
|
||||
"\n",
|
||||
"FLAGS = flags.FLAGS\n",
|
||||
"\n",
|
||||
"flags.DEFINE_string('output_proto', '',\n",
|
||||
" 'Output file to write the cp_model proto to.')\n",
|
||||
"flags.DEFINE_string('params', '', 'Sat solver parameters.')\n",
|
||||
"flags.DEFINE_string('params', 'max_time_in_seconds:10.0',\n",
|
||||
" 'Sat solver parameters.')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def negated_bounded_span(works, start, length):\n",
|
||||
|
||||
@@ -81,31 +81,25 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Solves the Stell Mill Slab problem with 4 different techniques.\"\"\"\n",
|
||||
"\n",
|
||||
"# overloaded sum() clashes with pytype.\n",
|
||||
"# pytype: disable=wrong-arg-types\n",
|
||||
"\n",
|
||||
"import argparse\n",
|
||||
"import collections\n",
|
||||
"import time\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"from absl import app\n",
|
||||
"from absl import flags\n",
|
||||
"from ortools.linear_solver import pywraplp\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"PARSER = argparse.ArgumentParser()\n",
|
||||
"FLAGS = flags.FLAGS\n",
|
||||
"\n",
|
||||
"PARSER.add_argument(\n",
|
||||
" '--problem', default=2, type=int, help='Problem id to solve.')\n",
|
||||
"PARSER.add_argument(\n",
|
||||
" '--break_symmetries',\n",
|
||||
" default=True,\n",
|
||||
" type=bool,\n",
|
||||
" help='Break symmetries between equivalent orders.')\n",
|
||||
"PARSER.add_argument(\n",
|
||||
" '--solver',\n",
|
||||
" default='sat_table',\n",
|
||||
" help='Method used to solve: sat, sat_table, sat_column, mip_column.')\n",
|
||||
"PARSER.add_argument(\n",
|
||||
" '--output_proto',\n",
|
||||
" default='',\n",
|
||||
" help='Output file to write the cp_model proto to.')\n",
|
||||
"flags.DEFINE_integer('problem', 2, 'Problem id to solve.')\n",
|
||||
"flags.DEFINE_boolean('break_symmetries', True,\n",
|
||||
" 'Break symmetries between equivalent orders.')\n",
|
||||
"flags.DEFINE_string(\n",
|
||||
" 'solver', 'mip_column', 'Method used to solve: sat, sat_table, sat_column, '\n",
|
||||
" 'mip_column.')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def build_problem(problem_id):\n",
|
||||
@@ -364,7 +358,7 @@
|
||||
" print(line)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def steel_mill_slab(problem, break_symmetries, output_proto):\n",
|
||||
"def steel_mill_slab(problem, break_symmetries):\n",
|
||||
" \"\"\"Solves the Steel Mill Slab Problem.\"\"\"\n",
|
||||
" ### Load problem.\n",
|
||||
" (num_slabs, capacities, num_colors, orders) = build_problem(problem)\n",
|
||||
@@ -377,17 +371,18 @@
|
||||
" print('Solving steel mill with %i orders, %i slabs, and %i capacities' %\n",
|
||||
" (num_orders, num_slabs, num_capacities - 1))\n",
|
||||
"\n",
|
||||
" # Compute auxilliary data.\n",
|
||||
" # Compute auxiliary data.\n",
|
||||
" widths = [x[0] for x in orders]\n",
|
||||
" colors = [x[1] for x in orders]\n",
|
||||
" max_capacity = max(capacities)\n",
|
||||
" loss_array = [\n",
|
||||
" min(x for x in capacities if x >= c) - c\n",
|
||||
" for c in range(max_capacity + 1)\n",
|
||||
" min(x for x in capacities if x >= c) - c for c in range(max_capacity +\n",
|
||||
" 1)\n",
|
||||
" ]\n",
|
||||
" max_loss = max(loss_array)\n",
|
||||
" orders_per_color = [[o for o in all_orders if colors[o] == c + 1]\n",
|
||||
" for c in all_colors]\n",
|
||||
" orders_per_color = [\n",
|
||||
" [o for o in all_orders if colors[o] == c + 1] for c in all_colors\n",
|
||||
" ]\n",
|
||||
" unique_color_orders = [\n",
|
||||
" o for o in all_orders if len(orders_per_color[colors[o] - 1]) == 1\n",
|
||||
" ]\n",
|
||||
@@ -404,14 +399,12 @@
|
||||
" for s in all_slabs\n",
|
||||
" ]\n",
|
||||
" color_is_in_slab = [[\n",
|
||||
" model.NewBoolVar('color_%i_in_slab_%i' % (c + 1, s))\n",
|
||||
" for c in all_colors\n",
|
||||
" model.NewBoolVar('color_%i_in_slab_%i' % (c + 1, s)) for c in all_colors\n",
|
||||
" ] for s in all_slabs]\n",
|
||||
"\n",
|
||||
" # Compute load of all slabs.\n",
|
||||
" for s in all_slabs:\n",
|
||||
" model.Add(\n",
|
||||
" sum(assign[o][s] * widths[o] for o in all_orders) == loads[s])\n",
|
||||
" model.Add(sum(assign[o][s] * widths[o] for o in all_orders) == loads[s])\n",
|
||||
"\n",
|
||||
" # Orders are assigned to one slab.\n",
|
||||
" for o in all_orders:\n",
|
||||
@@ -503,8 +496,9 @@
|
||||
"\n",
|
||||
" ### Output the solution.\n",
|
||||
" if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):\n",
|
||||
" print('Loss = %i, time = %f s, %i conflicts' % (\n",
|
||||
" solver.ObjectiveValue(), solver.WallTime(), solver.NumConflicts()))\n",
|
||||
" print(\n",
|
||||
" 'Loss = %i, time = %f s, %i conflicts' %\n",
|
||||
" (solver.ObjectiveValue(), solver.WallTime(), solver.NumConflicts()))\n",
|
||||
" else:\n",
|
||||
" print('No solution')\n",
|
||||
"\n",
|
||||
@@ -527,19 +521,19 @@
|
||||
" if assignment.load + new_width > max_capacity:\n",
|
||||
" continue\n",
|
||||
" new_colors = list(assignment.colors)\n",
|
||||
" if not new_color in new_colors:\n",
|
||||
" if new_color not in new_colors:\n",
|
||||
" new_colors.append(new_color)\n",
|
||||
" if len(new_colors) > 2:\n",
|
||||
" continue\n",
|
||||
" new_assignment = valid_assignment(\n",
|
||||
" orders=assignment.orders + [order_id],\n",
|
||||
" load=assignment.load + new_width,\n",
|
||||
" colors=new_colors)\n",
|
||||
" new_assignment = valid_assignment(orders=assignment.orders +\n",
|
||||
" [order_id],\n",
|
||||
" load=assignment.load + new_width,\n",
|
||||
" colors=new_colors)\n",
|
||||
" new_assignments.append(new_assignment)\n",
|
||||
" all_valid_assignments.extend(new_assignments)\n",
|
||||
"\n",
|
||||
" print('%i assignments created in %.2f s' % (len(all_valid_assignments),\n",
|
||||
" time.time() - start_time))\n",
|
||||
" print('%i assignments created in %.2f s' %\n",
|
||||
" (len(all_valid_assignments), time.time() - start_time))\n",
|
||||
" tuples = []\n",
|
||||
" for assignment in all_valid_assignments:\n",
|
||||
" solution = [0 for _ in range(len(colors))]\n",
|
||||
@@ -552,7 +546,7 @@
|
||||
" return tuples\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def steel_mill_slab_with_valid_slabs(problem, break_symmetries, output_proto):\n",
|
||||
"def steel_mill_slab_with_valid_slabs(problem, break_symmetries):\n",
|
||||
" \"\"\"Solves the Steel Mill Slab Problem.\"\"\"\n",
|
||||
" ### Load problem.\n",
|
||||
" (num_slabs, capacities, num_colors, orders) = build_problem(problem)\n",
|
||||
@@ -565,13 +559,13 @@
|
||||
" print('Solving steel mill with %i orders, %i slabs, and %i capacities' %\n",
|
||||
" (num_orders, num_slabs, num_capacities - 1))\n",
|
||||
"\n",
|
||||
" # Compute auxilliary data.\n",
|
||||
" # Compute auxiliary data.\n",
|
||||
" widths = [x[0] for x in orders]\n",
|
||||
" colors = [x[1] for x in orders]\n",
|
||||
" max_capacity = max(capacities)\n",
|
||||
" loss_array = [\n",
|
||||
" min(x for x in capacities if x >= c) - c\n",
|
||||
" for c in range(max_capacity + 1)\n",
|
||||
" min(x for x in capacities if x >= c) - c for c in range(max_capacity +\n",
|
||||
" 1)\n",
|
||||
" ]\n",
|
||||
" max_loss = max(loss_array)\n",
|
||||
"\n",
|
||||
@@ -582,21 +576,18 @@
|
||||
" assign = [[\n",
|
||||
" model.NewBoolVar('assign_%i_to_slab_%i' % (o, s)) for s in all_slabs\n",
|
||||
" ] for o in all_orders]\n",
|
||||
" loads = [\n",
|
||||
" model.NewIntVar(0, max_capacity, 'load_%i' % s) for s in all_slabs\n",
|
||||
" ]\n",
|
||||
" loads = [model.NewIntVar(0, max_capacity, 'load_%i' % s) for s in all_slabs]\n",
|
||||
" losses = [model.NewIntVar(0, max_loss, 'loss_%i' % s) for s in all_slabs]\n",
|
||||
"\n",
|
||||
" unsorted_valid_slabs = collect_valid_slabs_dp(capacities, colors, widths,\n",
|
||||
" loss_array)\n",
|
||||
" # Sort slab by descending load/loss. Remove duplicates.\n",
|
||||
" valid_slabs = sorted(\n",
|
||||
" unsorted_valid_slabs, key=lambda c: 1000 * c[-1] + c[-2])\n",
|
||||
" valid_slabs = sorted(unsorted_valid_slabs,\n",
|
||||
" key=lambda c: 1000 * c[-1] + c[-2])\n",
|
||||
"\n",
|
||||
" for s in all_slabs:\n",
|
||||
" model.AddAllowedAssignments(\n",
|
||||
" [assign[o][s] for o in all_orders] + [losses[s], loads[s]],\n",
|
||||
" valid_slabs)\n",
|
||||
" model.AddAllowedAssignments([assign[o][s] for o in all_orders] +\n",
|
||||
" [losses[s], loads[s]], valid_slabs)\n",
|
||||
"\n",
|
||||
" # Orders are assigned to one slab.\n",
|
||||
" for o in all_orders:\n",
|
||||
@@ -614,8 +605,9 @@
|
||||
" print('Breaking symmetries')\n",
|
||||
" width_to_unique_color_order = {}\n",
|
||||
" ordered_equivalent_orders = []\n",
|
||||
" orders_per_color = [[o for o in all_orders if colors[o] == c + 1]\n",
|
||||
" for c in all_colors]\n",
|
||||
" orders_per_color = [\n",
|
||||
" [o for o in all_orders if colors[o] == c + 1] for c in all_colors\n",
|
||||
" ]\n",
|
||||
" for c in all_colors:\n",
|
||||
" colored_orders = orders_per_color[c]\n",
|
||||
" if not colored_orders:\n",
|
||||
@@ -637,8 +629,7 @@
|
||||
" for w, os in local_width_to_order.items():\n",
|
||||
" if len(os) > 1:\n",
|
||||
" for p in range(len(os) - 1):\n",
|
||||
" ordered_equivalent_orders.append((os[p],\n",
|
||||
" os[p + 1]))\n",
|
||||
" ordered_equivalent_orders.append((os[p], os[p + 1]))\n",
|
||||
" for w, os in width_to_unique_color_order.items():\n",
|
||||
" if len(os) > 1:\n",
|
||||
" for p in range(len(os) - 1):\n",
|
||||
@@ -666,12 +657,6 @@
|
||||
"\n",
|
||||
" print('Model created')\n",
|
||||
"\n",
|
||||
" # Output model proto to file.\n",
|
||||
" if output_proto:\n",
|
||||
" output_file = open(output_proto, 'w')\n",
|
||||
" output_file.write(str(model.Proto()))\n",
|
||||
" output_file.close()\n",
|
||||
"\n",
|
||||
" ### Solve model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" solver.num_search_workers = 8\n",
|
||||
@@ -681,13 +666,14 @@
|
||||
"\n",
|
||||
" ### Output the solution.\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" print('Loss = %i, time = %.2f s, %i conflicts' % (\n",
|
||||
" solver.ObjectiveValue(), solver.WallTime(), solver.NumConflicts()))\n",
|
||||
" print(\n",
|
||||
" 'Loss = %i, time = %.2f s, %i conflicts' %\n",
|
||||
" (solver.ObjectiveValue(), solver.WallTime(), solver.NumConflicts()))\n",
|
||||
" else:\n",
|
||||
" print('No solution')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def steel_mill_slab_with_column_generation(problem, output_proto):\n",
|
||||
"def steel_mill_slab_with_column_generation(problem):\n",
|
||||
" \"\"\"Solves the Steel Mill Slab Problem.\"\"\"\n",
|
||||
" ### Load problem.\n",
|
||||
" (num_slabs, capacities, _, orders) = build_problem(problem)\n",
|
||||
@@ -698,13 +684,13 @@
|
||||
" print('Solving steel mill with %i orders, %i slabs, and %i capacities' %\n",
|
||||
" (num_orders, num_slabs, num_capacities - 1))\n",
|
||||
"\n",
|
||||
" # Compute auxilliary data.\n",
|
||||
" # Compute auxiliary data.\n",
|
||||
" widths = [x[0] for x in orders]\n",
|
||||
" colors = [x[1] for x in orders]\n",
|
||||
" max_capacity = max(capacities)\n",
|
||||
" loss_array = [\n",
|
||||
" min(x for x in capacities if x >= c) - c\n",
|
||||
" for c in range(max_capacity + 1)\n",
|
||||
" min(x for x in capacities if x >= c) - c for c in range(max_capacity +\n",
|
||||
" 1)\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" ### Model problem.\n",
|
||||
@@ -714,8 +700,8 @@
|
||||
" loss_array)\n",
|
||||
"\n",
|
||||
" # Sort slab by descending load/loss. Remove duplicates.\n",
|
||||
" valid_slabs = sorted(\n",
|
||||
" unsorted_valid_slabs, key=lambda c: 1000 * c[-1] + c[-2])\n",
|
||||
" valid_slabs = sorted(unsorted_valid_slabs,\n",
|
||||
" key=lambda c: 1000 * c[-1] + c[-2])\n",
|
||||
" all_valid_slabs = range(len(valid_slabs))\n",
|
||||
"\n",
|
||||
" # create model and decision variables.\n",
|
||||
@@ -724,7 +710,8 @@
|
||||
"\n",
|
||||
" for order_id in all_orders:\n",
|
||||
" model.Add(\n",
|
||||
" sum(selected[i] for i, slab in enumerate(valid_slabs)\n",
|
||||
" sum(selected[i]\n",
|
||||
" for i, slab in enumerate(valid_slabs)\n",
|
||||
" if slab[order_id]) == 1)\n",
|
||||
"\n",
|
||||
" # Redundant constraint (sum of loads == sum of widths).\n",
|
||||
@@ -738,22 +725,18 @@
|
||||
"\n",
|
||||
" print('Model created')\n",
|
||||
"\n",
|
||||
" # Output model proto to file.\n",
|
||||
" if output_proto:\n",
|
||||
" output_file = open(output_proto, 'w')\n",
|
||||
" output_file.write(str(model.Proto()))\n",
|
||||
" output_file.close()\n",
|
||||
"\n",
|
||||
" ### Solve model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" solver.parameters.num_search_workers = 8\n",
|
||||
" solver.parameters.log_search_progress = True\n",
|
||||
" solution_printer = cp_model.ObjectiveSolutionPrinter()\n",
|
||||
" status = solver.SolveWithSolutionCallback(model, solution_printer)\n",
|
||||
"\n",
|
||||
" ### Output the solution.\n",
|
||||
" if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):\n",
|
||||
" print('Loss = %i, time = %.2f s, %i conflicts' % (\n",
|
||||
" solver.ObjectiveValue(), solver.WallTime(), solver.NumConflicts()))\n",
|
||||
" print(\n",
|
||||
" 'Loss = %i, time = %.2f s, %i conflicts' %\n",
|
||||
" (solver.ObjectiveValue(), solver.WallTime(), solver.NumConflicts()))\n",
|
||||
" else:\n",
|
||||
" print('No solution')\n",
|
||||
"\n",
|
||||
@@ -769,13 +752,13 @@
|
||||
" print('Solving steel mill with %i orders, %i slabs, and %i capacities' %\n",
|
||||
" (num_orders, num_slabs, num_capacities - 1))\n",
|
||||
"\n",
|
||||
" # Compute auxilliary data.\n",
|
||||
" # Compute auxiliary data.\n",
|
||||
" widths = [x[0] for x in orders]\n",
|
||||
" colors = [x[1] for x in orders]\n",
|
||||
" max_capacity = max(capacities)\n",
|
||||
" loss_array = [\n",
|
||||
" min(x for x in capacities if x >= c) - c\n",
|
||||
" for c in range(max_capacity + 1)\n",
|
||||
" min(x for x in capacities if x >= c) - c for c in range(max_capacity +\n",
|
||||
" 1)\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" ### Model problem.\n",
|
||||
@@ -784,21 +767,22 @@
|
||||
" unsorted_valid_slabs = collect_valid_slabs_dp(capacities, colors, widths,\n",
|
||||
" loss_array)\n",
|
||||
" # Sort slab by descending load/loss. Remove duplicates.\n",
|
||||
" valid_slabs = sorted(\n",
|
||||
" unsorted_valid_slabs, key=lambda c: 1000 * c[-1] + c[-2])\n",
|
||||
" valid_slabs = sorted(unsorted_valid_slabs,\n",
|
||||
" key=lambda c: 1000 * c[-1] + c[-2])\n",
|
||||
" all_valid_slabs = range(len(valid_slabs))\n",
|
||||
"\n",
|
||||
" # create model and decision variables.\n",
|
||||
" start_time = time.time()\n",
|
||||
" solver = pywraplp.Solver('Steel',\n",
|
||||
" pywraplp.Solver.CBC_MIXED_INTEGER_PROGRAMMING)\n",
|
||||
" pywraplp.Solver.SCIP_MIXED_INTEGER_PROGRAMMING)\n",
|
||||
" selected = [\n",
|
||||
" solver.IntVar(0.0, 1.0, 'selected_%i' % i) for i in all_valid_slabs\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" for order in all_orders:\n",
|
||||
" solver.Add(\n",
|
||||
" sum(selected[i] for i in all_valid_slabs\n",
|
||||
" sum(selected[i]\n",
|
||||
" for i in all_valid_slabs\n",
|
||||
" if valid_slabs[i][order]) == 1)\n",
|
||||
"\n",
|
||||
" # Redundant constraint (sum of loads == sum of widths).\n",
|
||||
@@ -820,16 +804,14 @@
|
||||
" print('No solution')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"'''Main function'''\n",
|
||||
"if args.solver == 'sat':\n",
|
||||
" steel_mill_slab(args.problem, args.break_symmetries, args.output_proto)\n",
|
||||
"elif args.solver == 'sat_table':\n",
|
||||
" steel_mill_slab_with_valid_slabs(args.problem, args.break_symmetries,\n",
|
||||
" args.output_proto)\n",
|
||||
"elif args.solver == 'sat_column':\n",
|
||||
" steel_mill_slab_with_column_generation(args.problem, args.output_proto)\n",
|
||||
"if FLAGS.solver == 'sat':\n",
|
||||
" steel_mill_slab(FLAGS.problem, FLAGS.break_symmetries)\n",
|
||||
"elif FLAGS.solver == 'sat_table':\n",
|
||||
" steel_mill_slab_with_valid_slabs(FLAGS.problem, FLAGS.break_symmetries)\n",
|
||||
"elif FLAGS.solver == 'sat_column':\n",
|
||||
" steel_mill_slab_with_column_generation(FLAGS.problem)\n",
|
||||
"else: # 'mip_column'\n",
|
||||
" steel_mill_slab_with_mip_column_generation(args.problem)\n",
|
||||
" steel_mill_slab_with_mip_column_generation(FLAGS.problem)\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -83,7 +83,6 @@
|
||||
"# [START program]\n",
|
||||
"# [START import]\n",
|
||||
"from ortools.linear_solver import pywraplp\n",
|
||||
"\n",
|
||||
"# [END import]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -99,7 +98,6 @@
|
||||
" data['bin_capacity'] = 100\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# [END data_model]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -83,7 +83,6 @@
|
||||
"# [START program]\n",
|
||||
"# [START import]\n",
|
||||
"from ortools.linear_solver import pywraplp\n",
|
||||
"\n",
|
||||
"# [END import]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -83,7 +83,6 @@
|
||||
"# [START program]\n",
|
||||
"# [START import]\n",
|
||||
"from ortools.linear_solver import pywraplp\n",
|
||||
"\n",
|
||||
"# [END import]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -104,7 +103,6 @@
|
||||
" data['num_constraints'] = 4\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# [END data_model]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -83,7 +83,6 @@
|
||||
"# [START program]\n",
|
||||
"# [START import]\n",
|
||||
"from ortools.linear_solver import pywraplp\n",
|
||||
"\n",
|
||||
"# [END import]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -103,7 +102,6 @@
|
||||
" data['bin_capacities'] = [100, 100, 100, 100, 100]\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# [END data_model]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Solves a binpacking problem using the CP-SAT solver.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Code sample to demonstrates a simple Boolean constraint.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Code sample that encodes the product of two Boolean variables.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Link integer constraints together.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Encodes an convex piecewise linear function.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Code sample to demonstrates how to build an interval.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Code sample to demonstrate Boolean variable and literals.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -82,7 +82,6 @@
|
||||
"\"\"\"Minimal jobshop example.\"\"\"\n",
|
||||
"\n",
|
||||
"# [START program]\n",
|
||||
"\n",
|
||||
"import collections\n",
|
||||
"\n",
|
||||
"# [START model]\n",
|
||||
|
||||
@@ -83,9 +83,7 @@
|
||||
"\"\"\"Solves a multiple knapsack problem using the CP-SAT solver.\"\"\"\n",
|
||||
"\n",
|
||||
"# [START import]\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"# [END import]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -104,7 +102,6 @@
|
||||
" data['all_bins'] = range(data['num_bins'])\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# [END data_model]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -130,7 +127,6 @@
|
||||
" print('Total packed weight:', total_weight)\n",
|
||||
" print('Total packed value:', total_value)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# [END solution_printer]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Code sample to demonstrate how to build a NoOverlap constraint.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -84,7 +84,6 @@
|
||||
"# [START program]\n",
|
||||
"# [START import]\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"# [END import]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -120,7 +119,6 @@
|
||||
" def solution_count(self):\n",
|
||||
" return self._solution_count\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# [END solution_printer]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Code sample to demonstrates how to build an optional interval.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Rabbits and Pheasants quizz.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Code sample to demonstrates how to rank intervals.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Simple model with a reified constraint.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Code sample to demonstrate how an interval can span across a break.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -82,7 +82,6 @@
|
||||
"\"\"\"Code sample that solves a model and displays all solutions.\"\"\"\n",
|
||||
"\n",
|
||||
"# [START program]\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -82,7 +82,6 @@
|
||||
"\"\"\"Simple solve.\"\"\"\n",
|
||||
"\n",
|
||||
"# [START program]\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -82,7 +82,6 @@
|
||||
"\"\"\"Code sample that solves a model using solution hinting.\"\"\"\n",
|
||||
"\n",
|
||||
"# [START program]\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -82,7 +82,6 @@
|
||||
"\"\"\"Solves an optimization problem and displays all intermediate solutions.\"\"\"\n",
|
||||
"\n",
|
||||
"# [START program]\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Solves a problem with a time limit.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Implements a step function.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
"# limitations under the License.\n",
|
||||
"\"\"\"Code sample that solves a model and displays a small number of solutions.\"\"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
Reference in New Issue
Block a user