diff --git a/examples/notebook/constraint_solver/cvrp_reload.ipynb b/examples/notebook/constraint_solver/cvrp_reload.ipynb index 82b8a28766..dce7f7058d 100644 --- a/examples/notebook/constraint_solver/cvrp_reload.ipynb +++ b/examples/notebook/constraint_solver/cvrp_reload.ipynb @@ -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" ] } diff --git a/examples/notebook/constraint_solver/tsp_cities.ipynb b/examples/notebook/constraint_solver/tsp_cities.ipynb index 0959509a43..c8a2e25270 100644 --- a/examples/notebook/constraint_solver/tsp_cities.ipynb +++ b/examples/notebook/constraint_solver/tsp_cities.ipynb @@ -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", diff --git a/examples/notebook/constraint_solver/vrp_global_span.ipynb b/examples/notebook/constraint_solver/vrp_global_span.ipynb index 38e296fa7b..b771473c7e 100644 --- a/examples/notebook/constraint_solver/vrp_global_span.ipynb +++ b/examples/notebook/constraint_solver/vrp_global_span.ipynb @@ -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", diff --git a/examples/notebook/constraint_solver/vrp_initial_routes.ipynb b/examples/notebook/constraint_solver/vrp_initial_routes.ipynb index 3af95990a6..d5506dc6ed 100644 --- a/examples/notebook/constraint_solver/vrp_initial_routes.ipynb +++ b/examples/notebook/constraint_solver/vrp_initial_routes.ipynb @@ -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", diff --git a/examples/notebook/constraint_solver/vrptw_store_solution_data.ipynb b/examples/notebook/constraint_solver/vrptw_store_solution_data.ipynb new file mode 100644 index 0000000000..89a8ab0076 --- /dev/null +++ b/examples/notebook/constraint_solver/vrptw_store_solution_data.ipynb @@ -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": [ + "\n", + "\n", + "\n", + "
\n", + "Run in Google Colab\n", + "\n", + "View source on GitHub\n", + "
" + ] + }, + { + "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 +} diff --git a/examples/notebook/examples/appointments.ipynb b/examples/notebook/examples/appointments.ipynb index 8648309fbc..4d446e0536 100644 --- a/examples/notebook/examples/appointments.ipynb +++ b/examples/notebook/examples/appointments.ipynb @@ -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" ] } diff --git a/examples/notebook/examples/flexible_job_shop_sat.ipynb b/examples/notebook/examples/flexible_job_shop_sat.ipynb index e9a042b0d4..8484b8e648 100644 --- a/examples/notebook/examples/flexible_job_shop_sat.ipynb +++ b/examples/notebook/examples/flexible_job_shop_sat.ipynb @@ -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", diff --git a/examples/notebook/examples/gate_scheduling_sat.ipynb b/examples/notebook/examples/gate_scheduling_sat.ipynb index a26e126b82..74ccbbe26d 100644 --- a/examples/notebook/examples/gate_scheduling_sat.ipynb +++ b/examples/notebook/examples/gate_scheduling_sat.ipynb @@ -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", diff --git a/examples/notebook/examples/golomb8.ipynb b/examples/notebook/examples/golomb8.ipynb index 22161c065d..f9cc123c83 100644 --- a/examples/notebook/examples/golomb8.ipynb +++ b/examples/notebook/examples/golomb8.ipynb @@ -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", diff --git a/examples/notebook/examples/jobshop_with_maintenance_sat.ipynb b/examples/notebook/examples/jobshop_with_maintenance_sat.ipynb index de7dd696c1..60ce238e99 100644 --- a/examples/notebook/examples/jobshop_with_maintenance_sat.ipynb +++ b/examples/notebook/examples/jobshop_with_maintenance_sat.ipynb @@ -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", diff --git a/examples/notebook/examples/shift_scheduling_sat.ipynb b/examples/notebook/examples/shift_scheduling_sat.ipynb index f2f9f3d4d3..eee2b70dbb 100644 --- a/examples/notebook/examples/shift_scheduling_sat.ipynb +++ b/examples/notebook/examples/shift_scheduling_sat.ipynb @@ -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", diff --git a/examples/notebook/examples/steel_mill_slab_sat.ipynb b/examples/notebook/examples/steel_mill_slab_sat.ipynb index fda185f811..8b9ac1102a 100644 --- a/examples/notebook/examples/steel_mill_slab_sat.ipynb +++ b/examples/notebook/examples/steel_mill_slab_sat.ipynb @@ -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" ] } diff --git a/examples/notebook/linear_solver/bin_packing_mip.ipynb b/examples/notebook/linear_solver/bin_packing_mip.ipynb index 227b6f3059..2d0db0e172 100644 --- a/examples/notebook/linear_solver/bin_packing_mip.ipynb +++ b/examples/notebook/linear_solver/bin_packing_mip.ipynb @@ -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", diff --git a/examples/notebook/linear_solver/linear_programming_example.ipynb b/examples/notebook/linear_solver/linear_programming_example.ipynb index 85e5c39e85..8020f8ea6d 100644 --- a/examples/notebook/linear_solver/linear_programming_example.ipynb +++ b/examples/notebook/linear_solver/linear_programming_example.ipynb @@ -83,7 +83,6 @@ "# [START program]\n", "# [START import]\n", "from ortools.linear_solver import pywraplp\n", - "\n", "# [END import]\n", "\n", "\n", diff --git a/examples/notebook/linear_solver/mip_var_array.ipynb b/examples/notebook/linear_solver/mip_var_array.ipynb index 25aaf19556..272e65611d 100644 --- a/examples/notebook/linear_solver/mip_var_array.ipynb +++ b/examples/notebook/linear_solver/mip_var_array.ipynb @@ -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", diff --git a/examples/notebook/linear_solver/multiple_knapsack_mip.ipynb b/examples/notebook/linear_solver/multiple_knapsack_mip.ipynb index a6e0a0f258..6c1dd67a91 100644 --- a/examples/notebook/linear_solver/multiple_knapsack_mip.ipynb +++ b/examples/notebook/linear_solver/multiple_knapsack_mip.ipynb @@ -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", diff --git a/examples/notebook/sat/binpacking_problem_sat.ipynb b/examples/notebook/sat/binpacking_problem_sat.ipynb index 912fa5901f..edbb6c239a 100644 --- a/examples/notebook/sat/binpacking_problem_sat.ipynb +++ b/examples/notebook/sat/binpacking_problem_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/bool_or_sample_sat.ipynb b/examples/notebook/sat/bool_or_sample_sat.ipynb index 570e8b1353..62772cda82 100644 --- a/examples/notebook/sat/bool_or_sample_sat.ipynb +++ b/examples/notebook/sat/bool_or_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/boolean_product_sample_sat.ipynb b/examples/notebook/sat/boolean_product_sample_sat.ipynb index 89dae1846e..444a648990 100644 --- a/examples/notebook/sat/boolean_product_sample_sat.ipynb +++ b/examples/notebook/sat/boolean_product_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/channeling_sample_sat.ipynb b/examples/notebook/sat/channeling_sample_sat.ipynb index f62cfbc803..5f17927669 100644 --- a/examples/notebook/sat/channeling_sample_sat.ipynb +++ b/examples/notebook/sat/channeling_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/earliness_tardiness_cost_sample_sat.ipynb b/examples/notebook/sat/earliness_tardiness_cost_sample_sat.ipynb index 7fd069a85e..12a0bf2744 100644 --- a/examples/notebook/sat/earliness_tardiness_cost_sample_sat.ipynb +++ b/examples/notebook/sat/earliness_tardiness_cost_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/interval_sample_sat.ipynb b/examples/notebook/sat/interval_sample_sat.ipynb index ce0dbc64ac..00e77f5d01 100644 --- a/examples/notebook/sat/interval_sample_sat.ipynb +++ b/examples/notebook/sat/interval_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/literal_sample_sat.ipynb b/examples/notebook/sat/literal_sample_sat.ipynb index 1deff00c52..8ee48b1bab 100644 --- a/examples/notebook/sat/literal_sample_sat.ipynb +++ b/examples/notebook/sat/literal_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/minimal_jobshop_sat.ipynb b/examples/notebook/sat/minimal_jobshop_sat.ipynb index 5bf3b64b46..d627a86109 100644 --- a/examples/notebook/sat/minimal_jobshop_sat.ipynb +++ b/examples/notebook/sat/minimal_jobshop_sat.ipynb @@ -82,7 +82,6 @@ "\"\"\"Minimal jobshop example.\"\"\"\n", "\n", "# [START program]\n", - "\n", "import collections\n", "\n", "# [START model]\n", diff --git a/examples/notebook/sat/multiple_knapsack_sat.ipynb b/examples/notebook/sat/multiple_knapsack_sat.ipynb index 7ad99528b6..b54452eeec 100644 --- a/examples/notebook/sat/multiple_knapsack_sat.ipynb +++ b/examples/notebook/sat/multiple_knapsack_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/no_overlap_sample_sat.ipynb b/examples/notebook/sat/no_overlap_sample_sat.ipynb index 7ed4669ac2..3bf88bc104 100644 --- a/examples/notebook/sat/no_overlap_sample_sat.ipynb +++ b/examples/notebook/sat/no_overlap_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/nurses_sat.ipynb b/examples/notebook/sat/nurses_sat.ipynb index 598d83f792..008b33c231 100644 --- a/examples/notebook/sat/nurses_sat.ipynb +++ b/examples/notebook/sat/nurses_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/optional_interval_sample_sat.ipynb b/examples/notebook/sat/optional_interval_sample_sat.ipynb index 993fb7f4ef..956ea3060b 100644 --- a/examples/notebook/sat/optional_interval_sample_sat.ipynb +++ b/examples/notebook/sat/optional_interval_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/rabbits_and_pheasants_sat.ipynb b/examples/notebook/sat/rabbits_and_pheasants_sat.ipynb index 50636a369d..a7ceec3414 100644 --- a/examples/notebook/sat/rabbits_and_pheasants_sat.ipynb +++ b/examples/notebook/sat/rabbits_and_pheasants_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/ranking_sample_sat.ipynb b/examples/notebook/sat/ranking_sample_sat.ipynb index 58aa77799e..985b64e638 100644 --- a/examples/notebook/sat/ranking_sample_sat.ipynb +++ b/examples/notebook/sat/ranking_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/reified_sample_sat.ipynb b/examples/notebook/sat/reified_sample_sat.ipynb index 21bb868774..9e6cbd4988 100644 --- a/examples/notebook/sat/reified_sample_sat.ipynb +++ b/examples/notebook/sat/reified_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/scheduling_with_calendar_sample_sat.ipynb b/examples/notebook/sat/scheduling_with_calendar_sample_sat.ipynb index 57eb4ba96b..57f8330777 100644 --- a/examples/notebook/sat/scheduling_with_calendar_sample_sat.ipynb +++ b/examples/notebook/sat/scheduling_with_calendar_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/search_for_all_solutions_sample_sat.ipynb b/examples/notebook/sat/search_for_all_solutions_sample_sat.ipynb index 72304dfde3..dba2330b1e 100644 --- a/examples/notebook/sat/search_for_all_solutions_sample_sat.ipynb +++ b/examples/notebook/sat/search_for_all_solutions_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/simple_sat_program.ipynb b/examples/notebook/sat/simple_sat_program.ipynb index dd01be64f8..176bf92c68 100644 --- a/examples/notebook/sat/simple_sat_program.ipynb +++ b/examples/notebook/sat/simple_sat_program.ipynb @@ -82,7 +82,6 @@ "\"\"\"Simple solve.\"\"\"\n", "\n", "# [START program]\n", - "\n", "from ortools.sat.python import cp_model\n", "\n", "\n", diff --git a/examples/notebook/sat/solution_hinting_sample_sat.ipynb b/examples/notebook/sat/solution_hinting_sample_sat.ipynb index 9c630caa9d..896f5ebaea 100644 --- a/examples/notebook/sat/solution_hinting_sample_sat.ipynb +++ b/examples/notebook/sat/solution_hinting_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/solve_and_print_intermediate_solutions_sample_sat.ipynb b/examples/notebook/sat/solve_and_print_intermediate_solutions_sample_sat.ipynb index a35d8da88b..bc149f7b2e 100644 --- a/examples/notebook/sat/solve_and_print_intermediate_solutions_sample_sat.ipynb +++ b/examples/notebook/sat/solve_and_print_intermediate_solutions_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/solve_with_time_limit_sample_sat.ipynb b/examples/notebook/sat/solve_with_time_limit_sample_sat.ipynb index 3a0e022001..52838f980b 100644 --- a/examples/notebook/sat/solve_with_time_limit_sample_sat.ipynb +++ b/examples/notebook/sat/solve_with_time_limit_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/step_function_sample_sat.ipynb b/examples/notebook/sat/step_function_sample_sat.ipynb index df9ebe1693..96e8fe1a31 100644 --- a/examples/notebook/sat/step_function_sample_sat.ipynb +++ b/examples/notebook/sat/step_function_sample_sat.ipynb @@ -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", diff --git a/examples/notebook/sat/stop_after_n_solutions_sample_sat.ipynb b/examples/notebook/sat/stop_after_n_solutions_sample_sat.ipynb index 161185d552..8e702eb2b8 100644 --- a/examples/notebook/sat/stop_after_n_solutions_sample_sat.ipynb +++ b/examples/notebook/sat/stop_after_n_solutions_sample_sat.ipynb @@ -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",