update notebooks
This commit is contained in:
@@ -74,31 +74,31 @@
|
||||
"source": [
|
||||
"Capacitated Vehicle Routing Problem (CVRP).\n",
|
||||
"\n",
|
||||
" This is a sample using the routing library python wrapper to solve a CVRP\n",
|
||||
" problem while allowing multiple trips, i.e., vehicles can return to a depot\n",
|
||||
" to reset their load (\"reload\").\n",
|
||||
"This is a sample using the routing library python wrapper to solve a CVRP\n",
|
||||
"problem while allowing multiple trips, i.e., vehicles can return to a depot\n",
|
||||
"to reset their load (\"reload\").\n",
|
||||
"\n",
|
||||
" A description of the CVRP problem can be found here:\n",
|
||||
" http://en.wikipedia.org/wiki/Vehicle_routing_problem.\n",
|
||||
"A description of the CVRP problem can be found here:\n",
|
||||
"http://en.wikipedia.org/wiki/Vehicle_routing_problem.\n",
|
||||
"\n",
|
||||
" Distances are in meters.\n",
|
||||
"Distances are in meters.\n",
|
||||
"\n",
|
||||
" In order to implement multiple trips, new nodes are introduced at the same\n",
|
||||
" locations of the original depots. These additional nodes can be dropped\n",
|
||||
" from the schedule at 0 cost.\n",
|
||||
"In order to implement multiple trips, new nodes are introduced at the same\n",
|
||||
"locations of the original depots. These additional nodes can be dropped\n",
|
||||
"from the schedule at 0 cost.\n",
|
||||
"\n",
|
||||
" The max_slack parameter associated to the capacity constraints of all nodes\n",
|
||||
" can be set to be the maximum of the vehicles' capacities, rather than 0 like\n",
|
||||
" in a traditional CVRP. Slack is required since before a solution is found,\n",
|
||||
" it is not known how much capacity will be transferred at the new nodes. For\n",
|
||||
" all the other (original) nodes, the slack is then re-set to 0.\n",
|
||||
"The max_slack parameter associated to the capacity constraints of all nodes\n",
|
||||
"can be set to be the maximum of the vehicles' capacities, rather than 0 like\n",
|
||||
"in a traditional CVRP. Slack is required since before a solution is found,\n",
|
||||
"it is not known how much capacity will be transferred at the new nodes. For\n",
|
||||
"all the other (original) nodes, the slack is then re-set to 0.\n",
|
||||
"\n",
|
||||
" The above two considerations are implemented in `add_capacity_constraints()`.\n",
|
||||
"The above two considerations are implemented in `add_capacity_constraints()`.\n",
|
||||
"\n",
|
||||
" Last, it is useful to set a large distance between the initial depot and the\n",
|
||||
" new nodes introduced, to avoid schedules having spurious transits through\n",
|
||||
" those new nodes unless it's necessary to reload. This consideration is taken\n",
|
||||
" into account in `create_distance_evaluator()`.\n",
|
||||
"Last, it is useful to set a large distance between the initial depot and the\n",
|
||||
"new nodes introduced, to avoid schedules having spurious transits through\n",
|
||||
"those new nodes unless it's necessary to reload. This consideration is taken\n",
|
||||
"into account in `create_distance_evaluator()`.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
@@ -112,7 +112,7 @@
|
||||
"from functools import partial\n",
|
||||
"\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"###########################\n",
|
||||
@@ -145,52 +145,69 @@
|
||||
" (3, 7),\n",
|
||||
" (6, 7),\n",
|
||||
" (0, 8),\n",
|
||||
" (7, 8)\n",
|
||||
" (7, 8),\n",
|
||||
" ]\n",
|
||||
" # Compute locations in meters using the block dimension defined as follow\n",
|
||||
" # Manhattan average block: 750ft x 264ft -> 228m x 80m\n",
|
||||
" # here we use: 114m x 80m city block\n",
|
||||
" # src: https://nyti.ms/2GDoRIe 'NY Times: Know Your distance'\n",
|
||||
" data['locations'] = [(l[0] * 114, l[1] * 80) for l in _locations]\n",
|
||||
" data['num_locations'] = len(data['locations'])\n",
|
||||
" data['demands'] = \\\n",
|
||||
" [0, # depot\n",
|
||||
" -_capacity, # unload depot_first\n",
|
||||
" -_capacity, # unload depot_second\n",
|
||||
" -_capacity, # unload depot_third\n",
|
||||
" -_capacity, # unload depot_fourth\n",
|
||||
" -_capacity, # unload depot_fifth\n",
|
||||
" 3, 3, # 1, 2\n",
|
||||
" 3, 4, # 3, 4\n",
|
||||
" 3, 4, # 5, 6\n",
|
||||
" 8, 8, # 7, 8\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",
|
||||
" data['time_windows'] = \\\n",
|
||||
" [(0, 0), # depot\n",
|
||||
" (0, 1000), # unload depot_first\n",
|
||||
" (0, 1000), # unload depot_second\n",
|
||||
" (0, 1000), # unload depot_third\n",
|
||||
" (0, 1000), # unload depot_fourth\n",
|
||||
" (0, 1000), # unload depot_fifth\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",
|
||||
" data[\"locations\"] = [(l[0] * 114, l[1] * 80) for l in _locations]\n",
|
||||
" data[\"num_locations\"] = len(data[\"locations\"])\n",
|
||||
" data[\"demands\"] = [\n",
|
||||
" 0, # depot\n",
|
||||
" -_capacity, # unload depot_first\n",
|
||||
" -_capacity, # unload depot_second\n",
|
||||
" -_capacity, # unload depot_third\n",
|
||||
" -_capacity, # unload depot_fourth\n",
|
||||
" -_capacity, # unload depot_fifth\n",
|
||||
" 3,\n",
|
||||
" 3, # 1, 2\n",
|
||||
" 3,\n",
|
||||
" 4, # 3, 4\n",
|
||||
" 3,\n",
|
||||
" 4, # 5, 6\n",
|
||||
" 8,\n",
|
||||
" 8, # 7, 8\n",
|
||||
" 3,\n",
|
||||
" 3, # 9,10\n",
|
||||
" 3,\n",
|
||||
" 3, # 11,12\n",
|
||||
" 4,\n",
|
||||
" 4, # 13, 14\n",
|
||||
" 8,\n",
|
||||
" 8,\n",
|
||||
" ] # 15, 16\n",
|
||||
" data[\"time_per_demand_unit\"] = 5 # 5 minutes/unit\n",
|
||||
" data[\"time_windows\"] = [\n",
|
||||
" (0, 0), # depot\n",
|
||||
" (0, 1000), # unload depot_first\n",
|
||||
" (0, 1000), # unload depot_second\n",
|
||||
" (0, 1000), # unload depot_third\n",
|
||||
" (0, 1000), # unload depot_fourth\n",
|
||||
" (0, 1000), # unload depot_fifth\n",
|
||||
" (75, 850),\n",
|
||||
" (75, 850), # 1, 2\n",
|
||||
" (60, 700),\n",
|
||||
" (45, 550), # 3, 4\n",
|
||||
" (0, 800),\n",
|
||||
" (50, 600), # 5, 6\n",
|
||||
" (0, 1000),\n",
|
||||
" (10, 200), # 7, 8\n",
|
||||
" (0, 1000),\n",
|
||||
" (75, 850), # 9, 10\n",
|
||||
" (85, 950),\n",
|
||||
" (5, 150), # 11, 12\n",
|
||||
" (15, 250),\n",
|
||||
" (10, 200), # 13, 14\n",
|
||||
" (45, 550),\n",
|
||||
" (30, 400),\n",
|
||||
" ] # 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[\"vehicle_speed\"] = 5 * 60 / 3.6 # Travel speed: 5km/h to convert in m/min\n",
|
||||
" data[\"depot\"] = 0\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -199,30 +216,29 @@
|
||||
"#######################\n",
|
||||
"def manhattan_distance(position_1, position_2):\n",
|
||||
" \"\"\"Computes the Manhattan distance between two points\"\"\"\n",
|
||||
" return (abs(position_1[0] - position_2[0]) +\n",
|
||||
" abs(position_1[1] - position_2[1]))\n",
|
||||
" return abs(position_1[0] - position_2[0]) + abs(position_1[1] - position_2[1])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_distance_evaluator(data):\n",
|
||||
" \"\"\"Creates callback to return distance between points.\"\"\"\n",
|
||||
" _distances = {}\n",
|
||||
" # precompute distance between location to have distance callback in O(1)\n",
|
||||
" for from_node in range(data['num_locations']):\n",
|
||||
" for from_node in range(data[\"num_locations\"]):\n",
|
||||
" _distances[from_node] = {}\n",
|
||||
" for to_node in range(data['num_locations']):\n",
|
||||
" 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",
|
||||
" _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",
|
||||
" _distances[from_node][to_node] = manhattan_distance(\n",
|
||||
" data[\"locations\"][from_node], data[\"locations\"][to_node]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" def distance_evaluator(manager, from_node, to_node):\n",
|
||||
" \"\"\"Returns the manhattan distance between the two nodes\"\"\"\n",
|
||||
" return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(\n",
|
||||
" to_node)]\n",
|
||||
" return _distances[manager.IndexToNode(from_node)][manager.IndexToNode(to_node)]\n",
|
||||
"\n",
|
||||
" return distance_evaluator\n",
|
||||
"\n",
|
||||
@@ -230,13 +246,14 @@
|
||||
"def add_distance_dimension(routing, manager, data, distance_evaluator_index):\n",
|
||||
" \"\"\"Add Global Span constraint\"\"\"\n",
|
||||
" del manager\n",
|
||||
" distance = 'Distance'\n",
|
||||
" distance = \"Distance\"\n",
|
||||
" routing.AddDimension(\n",
|
||||
" distance_evaluator_index,\n",
|
||||
" 0, # null slack\n",
|
||||
" data['vehicle_max_distance'], # maximum distance per vehicle\n",
|
||||
" data[\"vehicle_max_distance\"], # maximum distance per vehicle\n",
|
||||
" True, # start cumul to zero\n",
|
||||
" distance)\n",
|
||||
" distance,\n",
|
||||
" )\n",
|
||||
" distance_dimension = routing.GetDimensionOrDie(distance)\n",
|
||||
" # Try to minimize the max distance among vehicles.\n",
|
||||
" # /!\\ It doesn't mean the standard deviation is minimized\n",
|
||||
@@ -245,7 +262,7 @@
|
||||
"\n",
|
||||
"def create_demand_evaluator(data):\n",
|
||||
" \"\"\"Creates callback to get demands at each location.\"\"\"\n",
|
||||
" _demands = data['demands']\n",
|
||||
" _demands = data[\"demands\"]\n",
|
||||
"\n",
|
||||
" def demand_evaluator(manager, from_node):\n",
|
||||
" \"\"\"Returns the demand of the current node\"\"\"\n",
|
||||
@@ -256,14 +273,15 @@
|
||||
"\n",
|
||||
"def add_capacity_constraints(routing, manager, data, demand_evaluator_index):\n",
|
||||
" \"\"\"Adds capacity constraint\"\"\"\n",
|
||||
" vehicle_capacity = data['vehicle_capacity']\n",
|
||||
" capacity = 'Capacity'\n",
|
||||
" vehicle_capacity = data[\"vehicle_capacity\"]\n",
|
||||
" capacity = \"Capacity\"\n",
|
||||
" routing.AddDimension(\n",
|
||||
" demand_evaluator_index,\n",
|
||||
" vehicle_capacity,\n",
|
||||
" vehicle_capacity,\n",
|
||||
" True, # start cumul to zero\n",
|
||||
" capacity)\n",
|
||||
" capacity,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Add Slack for reseting to zero unload depot nodes.\n",
|
||||
" # e.g. vehicle with load 10/15 arrives at node 1 (depot unload)\n",
|
||||
@@ -275,7 +293,7 @@
|
||||
" routing.AddDisjunction([node_index], 0)\n",
|
||||
"\n",
|
||||
" # Allow to drop regular node with a cost.\n",
|
||||
" for node in range(6, len(data['demands'])):\n",
|
||||
" for node in range(6, len(data[\"demands\"])):\n",
|
||||
" node_index = manager.NodeToIndex(node)\n",
|
||||
" capacity_dimension.SlackVar(node_index).SetValue(0)\n",
|
||||
" routing.AddDisjunction([node_index], 100_000)\n",
|
||||
@@ -286,52 +304,56 @@
|
||||
"\n",
|
||||
" def service_time(data, node):\n",
|
||||
" \"\"\"Gets the service time for the specified location.\"\"\"\n",
|
||||
" return abs(data['demands'][node]) * data['time_per_demand_unit']\n",
|
||||
" return abs(data[\"demands\"][node]) * data[\"time_per_demand_unit\"]\n",
|
||||
"\n",
|
||||
" def travel_time(data, from_node, to_node):\n",
|
||||
" \"\"\"Gets the travel times between two locations.\"\"\"\n",
|
||||
" if from_node == to_node:\n",
|
||||
" travel_time = 0\n",
|
||||
" else:\n",
|
||||
" travel_time = manhattan_distance(\n",
|
||||
" data['locations'][from_node],\n",
|
||||
" data['locations'][to_node]) / data['vehicle_speed']\n",
|
||||
" travel_time = (\n",
|
||||
" manhattan_distance(\n",
|
||||
" data[\"locations\"][from_node], data[\"locations\"][to_node]\n",
|
||||
" )\n",
|
||||
" / data[\"vehicle_speed\"]\n",
|
||||
" )\n",
|
||||
" return travel_time\n",
|
||||
"\n",
|
||||
" _total_time = {}\n",
|
||||
" # precompute total time to have time callback in O(1)\n",
|
||||
" for from_node in range(data['num_locations']):\n",
|
||||
" for from_node in range(data[\"num_locations\"]):\n",
|
||||
" _total_time[from_node] = {}\n",
|
||||
" for to_node in range(data['num_locations']):\n",
|
||||
" for to_node in range(data[\"num_locations\"]):\n",
|
||||
" if from_node == to_node:\n",
|
||||
" _total_time[from_node][to_node] = 0\n",
|
||||
" else:\n",
|
||||
" _total_time[from_node][to_node] = int(\n",
|
||||
" service_time(data, from_node) +\n",
|
||||
" travel_time(data, from_node, to_node))\n",
|
||||
" service_time(data, from_node)\n",
|
||||
" + travel_time(data, from_node, to_node)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" def time_evaluator(manager, from_node, to_node):\n",
|
||||
" \"\"\"Returns the total time between the two nodes\"\"\"\n",
|
||||
" return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode(\n",
|
||||
" to_node)]\n",
|
||||
" return _total_time[manager.IndexToNode(from_node)][manager.IndexToNode(to_node)]\n",
|
||||
"\n",
|
||||
" return time_evaluator\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def add_time_window_constraints(routing, manager, data, time_evaluator):\n",
|
||||
" \"\"\"Add Time windows constraint\"\"\"\n",
|
||||
" time = 'Time'\n",
|
||||
" max_time = data['vehicle_max_time']\n",
|
||||
" time = \"Time\"\n",
|
||||
" max_time = data[\"vehicle_max_time\"]\n",
|
||||
" routing.AddDimension(\n",
|
||||
" time_evaluator,\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,\n",
|
||||
" )\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(time)\n",
|
||||
" # Add time window constraints for each location except depot\n",
|
||||
" # and 'copy' the slack var in the solution object (aka Assignment) to print it\n",
|
||||
" for location_idx, time_window in enumerate(data['time_windows']):\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",
|
||||
@@ -339,70 +361,73 @@
|
||||
" routing.AddToAssignment(time_dimension.SlackVar(index))\n",
|
||||
" # Add time window constraints for each vehicle start node\n",
|
||||
" # and 'copy' the slack var in the solution object (aka Assignment) to print it\n",
|
||||
" for vehicle_id in range(data['num_vehicles']):\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",
|
||||
" time_dimension.CumulVar(index).SetRange(\n",
|
||||
" data[\"time_windows\"][0][0], data[\"time_windows\"][0][1]\n",
|
||||
" )\n",
|
||||
" routing.AddToAssignment(time_dimension.SlackVar(index))\n",
|
||||
" # Warning: Slack var is not defined for vehicle's end node\n",
|
||||
" #routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id)))\n",
|
||||
" # routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id)))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"###########\n",
|
||||
"# Printer #\n",
|
||||
"###########\n",
|
||||
"def print_solution(data, manager, routing, assignment): # pylint:disable=too-many-locals\n",
|
||||
"def print_solution(\n",
|
||||
" data, manager, routing, assignment\n",
|
||||
"): # pylint:disable=too-many-locals\n",
|
||||
" \"\"\"Prints assignment on console\"\"\"\n",
|
||||
" print(f'Objective: {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",
|
||||
" capacity_dimension = routing.GetDimensionOrDie(\"Capacity\")\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(\"Time\")\n",
|
||||
" dropped = []\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(f'dropped orders: {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",
|
||||
" print(f\"dropped reload stations: {dropped}\")\n",
|
||||
"\n",
|
||||
" for vehicle_id in range(data['num_vehicles']):\n",
|
||||
" for vehicle_id in range(data[\"num_vehicles\"]):\n",
|
||||
" index = routing.Start(vehicle_id)\n",
|
||||
" plan_output = f'Route for vehicle {vehicle_id}:\\n'\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",
|
||||
" time_var = time_dimension.CumulVar(index)\n",
|
||||
" plan_output += (\n",
|
||||
" f' {manager.IndexToNode(index)} '\n",
|
||||
" f'Load({assignment.Min(load_var)}) '\n",
|
||||
" f'Time({assignment.Min(time_var)},{assignment.Max(time_var)}) ->'\n",
|
||||
" f\" {manager.IndexToNode(index)} \"\n",
|
||||
" f\"Load({assignment.Min(load_var)}) \"\n",
|
||||
" f\"Time({assignment.Min(time_var)},{assignment.Max(time_var)}) ->\"\n",
|
||||
" )\n",
|
||||
" previous_index = index\n",
|
||||
" index = assignment.Value(routing.NextVar(index))\n",
|
||||
" distance += routing.GetArcCostForVehicle(previous_index, index,\n",
|
||||
" vehicle_id)\n",
|
||||
" distance += routing.GetArcCostForVehicle(previous_index, index, vehicle_id)\n",
|
||||
" load_var = capacity_dimension.CumulVar(index)\n",
|
||||
" time_var = time_dimension.CumulVar(index)\n",
|
||||
" plan_output += (\n",
|
||||
" f' {manager.IndexToNode(index)} '\n",
|
||||
" f'Load({assignment.Min(load_var)}) '\n",
|
||||
" f'Time({assignment.Min(time_var)},{assignment.Max(time_var)})\\n')\n",
|
||||
" plan_output += f'Distance of the route: {distance}m\\n'\n",
|
||||
" plan_output += f'Load of the route: {assignment.Min(load_var)}\\n'\n",
|
||||
" plan_output += f'Time of the route: {assignment.Min(time_var)}min\\n'\n",
|
||||
" f\" {manager.IndexToNode(index)} \"\n",
|
||||
" f\"Load({assignment.Min(load_var)}) \"\n",
|
||||
" f\"Time({assignment.Min(time_var)},{assignment.Max(time_var)})\\n\"\n",
|
||||
" )\n",
|
||||
" plan_output += f\"Distance of the route: {distance}m\\n\"\n",
|
||||
" plan_output += f\"Load of the route: {assignment.Min(load_var)}\\n\"\n",
|
||||
" plan_output += f\"Time of the route: {assignment.Min(time_var)}min\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += distance\n",
|
||||
" total_load += assignment.Min(load_var)\n",
|
||||
" total_time += assignment.Min(time_var)\n",
|
||||
" print(f'Total Distance of all routes: {total_distance}m')\n",
|
||||
" print(f'Total Load of all routes: {total_load}')\n",
|
||||
" print(f'Total Time of all routes: {total_time}min')\n",
|
||||
" print(f\"Total Distance of all routes: {total_distance}m\")\n",
|
||||
" print(f\"Total Load of all routes: {total_load}\")\n",
|
||||
" print(f\"Total Time of all routes: {total_time}min\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"########\n",
|
||||
@@ -414,15 +439,17 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(data['num_locations'],\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" data[\"num_locations\"], data[\"num_vehicles\"], data[\"depot\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
"\n",
|
||||
" # Define weight of each edge\n",
|
||||
" distance_evaluator_index = routing.RegisterTransitCallback(\n",
|
||||
" partial(create_distance_evaluator(data), manager))\n",
|
||||
" partial(create_distance_evaluator(data), manager)\n",
|
||||
" )\n",
|
||||
" routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)\n",
|
||||
"\n",
|
||||
" # Add Distance constraint to minimize the longuest route\n",
|
||||
@@ -430,20 +457,24 @@
|
||||
"\n",
|
||||
" # Add Capacity constraint\n",
|
||||
" demand_evaluator_index = routing.RegisterUnaryTransitCallback(\n",
|
||||
" partial(create_demand_evaluator(data), manager))\n",
|
||||
" partial(create_demand_evaluator(data), manager)\n",
|
||||
" )\n",
|
||||
" add_capacity_constraints(routing, manager, data, demand_evaluator_index)\n",
|
||||
"\n",
|
||||
" # Add Time Window constraint\n",
|
||||
" time_evaluator_index = routing.RegisterTransitCallback(\n",
|
||||
" partial(create_time_evaluator(data), manager))\n",
|
||||
" partial(create_time_evaluator(data), manager)\n",
|
||||
" )\n",
|
||||
" add_time_window_constraints(routing, manager, data, time_evaluator_index)\n",
|
||||
"\n",
|
||||
" # Setting first solution heuristic (cheapest addition).\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" ) # pylint: disable=no-member\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.time_limit.FromSeconds(3)\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -75,12 +75,12 @@
|
||||
"\n",
|
||||
"Capacitated Vehicle Routing Problem with Time Windows (CVRPTW).\n",
|
||||
"\n",
|
||||
" This is a sample using the routing library python wrapper to solve a CVRPTW\n",
|
||||
" problem.\n",
|
||||
" A description of the problem can be found here:\n",
|
||||
" http://en.wikipedia.org/wiki/Vehicle_routing_problem.\n",
|
||||
"This is a sample using the routing library python wrapper to solve a CVRPTW\n",
|
||||
"problem.\n",
|
||||
"A description of the problem can be found here:\n",
|
||||
"http://en.wikipedia.org/wiki/Vehicle_routing_problem.\n",
|
||||
"\n",
|
||||
" Distances are in meters and time in minutes.\n",
|
||||
"Distances are in meters and time in minutes.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
@@ -92,8 +92,8 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import functools\n",
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -386,7 +386,11 @@
|
||||
" vehicle_break = data[\"breaks\"][v]\n",
|
||||
" break_intervals[v] = [\n",
|
||||
" routing.solver().FixedDurationIntervalVar(\n",
|
||||
" 15, 100, vehicle_break[0], vehicle_break[1], f\"Break for vehicle {v}\"\n",
|
||||
" 15,\n",
|
||||
" 100,\n",
|
||||
" vehicle_break[0],\n",
|
||||
" vehicle_break[1],\n",
|
||||
" f\"Break for vehicle {v}\",\n",
|
||||
" )\n",
|
||||
" ]\n",
|
||||
" time_dimension.SetBreakIntervalsOfVehicle(\n",
|
||||
@@ -396,7 +400,7 @@
|
||||
" # Setting first solution heuristic (cheapest addition).\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" ) # pylint: disable=no-member\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -116,7 +116,7 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" ) # pylint: disable=no-member\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -87,8 +87,11 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"FirstSolutionStrategy = enums_pb2.FirstSolutionStrategy\n",
|
||||
"RoutingSearchStatus = enums_pb2.RoutingSearchStatus\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -140,16 +143,24 @@
|
||||
" return distance_callback\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(manager, routing, assignment):\n",
|
||||
"def print_solution(manager, routing, solution):\n",
|
||||
" \"\"\"Prints assignment on console.\"\"\"\n",
|
||||
" print(f\"Objective: {assignment.ObjectiveValue()}\")\n",
|
||||
" status = routing.status()\n",
|
||||
" print(f\"Status: {RoutingSearchStatus.Value.Name(status)}\")\n",
|
||||
" if (\n",
|
||||
" status != RoutingSearchStatus.ROUTING_OPTIMAL\n",
|
||||
" and status != RoutingSearchStatus.ROUTING_SUCCESS\n",
|
||||
" ):\n",
|
||||
" print(\"No solution found!\")\n",
|
||||
" return\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
" index = routing.Start(0)\n",
|
||||
" plan_output = \"Route for vehicle 0:\\n\"\n",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" plan_output += f\" {manager.IndexToNode(index)} ->\"\n",
|
||||
" previous_index = index\n",
|
||||
" index = assignment.Value(routing.NextVar(index))\n",
|
||||
" index = solution.Value(routing.NextVar(index))\n",
|
||||
" route_distance += routing.GetArcCostForVehicle(previous_index, index, 0)\n",
|
||||
" plan_output += f\" {manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
@@ -178,16 +189,13 @@
|
||||
"\n",
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.first_solution_strategy = FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" assignment = routing.SolveWithParameters(search_parameters)\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
"\n",
|
||||
" # Print solution on console.\n",
|
||||
" if assignment:\n",
|
||||
" print_solution(manager, routing, assignment)\n",
|
||||
" print_solution(manager, routing, solution)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -84,8 +84,8 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import math\n",
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -210,7 +210,7 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -155,7 +155,7 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -161,7 +161,7 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -75,12 +75,12 @@
|
||||
"\n",
|
||||
"Simple Vehicles Routing Problem (VRP).\n",
|
||||
"\n",
|
||||
" This is a sample using the routing library python wrapper to solve a VRP\n",
|
||||
" problem.\n",
|
||||
" A description of the problem can be found here:\n",
|
||||
" http://en.wikipedia.org/wiki/Vehicle_routing_problem.\n",
|
||||
"This is a sample using the routing library python wrapper to solve a VRP\n",
|
||||
"problem.\n",
|
||||
"A description of the problem can be found here:\n",
|
||||
"http://en.wikipedia.org/wiki/Vehicle_routing_problem.\n",
|
||||
"\n",
|
||||
" Distances are in meters.\n",
|
||||
"Distances are in meters.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
@@ -91,8 +91,11 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"FirstSolutionStrategy = enums_pb2.FirstSolutionStrategy\n",
|
||||
"RoutingSearchStatus = enums_pb2.RoutingSearchStatus\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -124,20 +127,28 @@
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(data, manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
"def print_solution(manager, routing, solution):\n",
|
||||
" \"\"\"Prints assignment on console.\"\"\"\n",
|
||||
" status = routing.status()\n",
|
||||
" print(f\"Status: {RoutingSearchStatus.Value.Name(status)}\")\n",
|
||||
" if (\n",
|
||||
" status != RoutingSearchStatus.ROUTING_OPTIMAL\n",
|
||||
" and status != RoutingSearchStatus.ROUTING_SUCCESS\n",
|
||||
" ):\n",
|
||||
" print(\"No solution found!\")\n",
|
||||
" return\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
" total_distance = 0\n",
|
||||
" for vehicle_id in range(data[\"num_vehicles\"]):\n",
|
||||
" index = routing.Start(vehicle_id)\n",
|
||||
" plan_output = f\"Route for vehicle {vehicle_id}:\\n\"\n",
|
||||
" for vehicle_index in range(manager.GetNumberOfVehicles()):\n",
|
||||
" index = routing.Start(vehicle_index)\n",
|
||||
" plan_output = f\"Route for vehicle {vehicle_index}:\\n\"\n",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" plan_output += f\" {manager.IndexToNode(index)} ->\"\n",
|
||||
" previous_index = index\n",
|
||||
" index = solution.Value(routing.NextVar(index))\n",
|
||||
" route_distance += routing.GetArcCostForVehicle(\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" previous_index, index, vehicle_index\n",
|
||||
" )\n",
|
||||
" plan_output += f\" {manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
@@ -146,7 +157,6 @@
|
||||
" print(f\"Total Distance of all routes: {total_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" \"\"\"Entry point of the program.\"\"\"\n",
|
||||
" # Instantiate the data problem.\n",
|
||||
@@ -175,18 +185,13 @@
|
||||
"\n",
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.first_solution_strategy = FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
"\n",
|
||||
" # Print solution on console.\n",
|
||||
" if solution:\n",
|
||||
" print_solution(data, manager, routing, solution)\n",
|
||||
" else:\n",
|
||||
" print(\"No solution found !\")\n",
|
||||
" print_solution(manager, routing, solution)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -75,12 +75,12 @@
|
||||
"\n",
|
||||
"Vehicle Routing Problem (VRP) with breaks.\n",
|
||||
"\n",
|
||||
" This is a sample using the routing library python wrapper to solve a VRP\n",
|
||||
" problem.\n",
|
||||
" A description of the problem can be found here:\n",
|
||||
" http://en.wikipedia.org/wiki/Vehicle_routing_problem.\n",
|
||||
"This is a sample using the routing library python wrapper to solve a VRP\n",
|
||||
"problem.\n",
|
||||
"A description of the problem can be found here:\n",
|
||||
"http://en.wikipedia.org/wiki/Vehicle_routing_problem.\n",
|
||||
"\n",
|
||||
" Durations are in minutes.\n",
|
||||
"Durations are in minutes.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
@@ -91,8 +91,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -224,10 +224,10 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" # search_parameters.log_search = True\n",
|
||||
" search_parameters.time_limit.FromSeconds(2)\n",
|
||||
|
||||
@@ -73,14 +73,15 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Vehicles Routing Problem (VRP) with breaks relative to the vehicle start time.\n",
|
||||
" Each vehicles start at T:15min, T:30min, T:45min and T:60min respectively.\n",
|
||||
"\n",
|
||||
" Each vehicle must perform a break lasting 5 minutes,\n",
|
||||
" starting between 25 and 45 minutes after route start.\n",
|
||||
" e.g. vehicle 2 starting a T:45min must start a 5min breaks\n",
|
||||
" between [45+25,45+45] i.e. in the range [70, 90].\n",
|
||||
"Each vehicles start at T:15min, T:30min, T:45min and T:60min respectively.\n",
|
||||
"\n",
|
||||
" Durations are in minutes.\n",
|
||||
"Each vehicle must perform a break lasting 5 minutes,\n",
|
||||
"starting between 25 and 45 minutes after route start.\n",
|
||||
"e.g. vehicle 2 starting a T:45min must start a 5min breaks\n",
|
||||
"between [45+25,45+45] i.e. in the range [70, 90].\n",
|
||||
"\n",
|
||||
"Durations are in minutes.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
@@ -91,16 +92,17 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" data['num_vehicles'] = 4\n",
|
||||
" data['depot'] = 0\n",
|
||||
" data['time_matrix'] = [\n",
|
||||
" data[\"num_vehicles\"] = 4\n",
|
||||
" data[\"depot\"] = 0\n",
|
||||
" data[\"time_matrix\"] = [\n",
|
||||
" [0, 27, 38, 34, 29, 13, 25, 9, 15, 9, 26, 25, 19, 17, 23, 38, 33],\n",
|
||||
" [27, 0, 34, 15, 9, 25, 36, 17, 34, 37, 54, 29, 24, 33, 50, 43, 60],\n",
|
||||
" [38, 34, 0, 49, 43, 25, 13, 40, 23, 37, 20, 63, 58, 56, 39, 77, 37],\n",
|
||||
@@ -120,46 +122,48 @@
|
||||
" [33, 60, 37, 67, 62, 35, 24, 42, 25, 23, 17, 42, 36, 26, 9, 39, 0],\n",
|
||||
" ]\n",
|
||||
" # 15 min of service time\n",
|
||||
" data['service_time'] = [15] * len(data['time_matrix'])\n",
|
||||
" data['service_time'][data['depot']] = 0\n",
|
||||
" assert len(data['time_matrix']) == len(data['service_time'])\n",
|
||||
" data[\"service_time\"] = [15] * len(data[\"time_matrix\"])\n",
|
||||
" data[\"service_time\"][data[\"depot\"]] = 0\n",
|
||||
" assert len(data[\"time_matrix\"]) == len(data[\"service_time\"])\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {solution.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
"\n",
|
||||
" print('Breaks:')\n",
|
||||
" print(\"Breaks:\")\n",
|
||||
" intervals = solution.IntervalVarContainer()\n",
|
||||
" for i in range(intervals.Size()):\n",
|
||||
" brk = intervals.Element(i)\n",
|
||||
" if brk.PerformedValue() == 1:\n",
|
||||
" print(f'{brk.Var().Name()}: ' +\n",
|
||||
" f'Start({brk.StartValue()}) Duration({brk.DurationValue()})')\n",
|
||||
" print(\n",
|
||||
" f\"{brk.Var().Name()}: \"\n",
|
||||
" + f\"Start({brk.StartValue()}) Duration({brk.DurationValue()})\"\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print(f'{brk.Var().Name()}: Unperformed')\n",
|
||||
" print(f\"{brk.Var().Name()}: Unperformed\")\n",
|
||||
"\n",
|
||||
" time_dimension = routing.GetDimensionOrDie('Time')\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(\"Time\")\n",
|
||||
" total_time = 0\n",
|
||||
" for vehicle_id in range(manager.GetNumberOfVehicles()):\n",
|
||||
" index = routing.Start(vehicle_id)\n",
|
||||
" plan_output = f'Route for vehicle {vehicle_id}:\\n'\n",
|
||||
" plan_output = f\"Route for vehicle {vehicle_id}:\\n\"\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" time_var = time_dimension.CumulVar(index)\n",
|
||||
" if routing.IsStart(index):\n",
|
||||
" start_time = solution.Value(time_var)\n",
|
||||
" plan_output += f'{manager.IndexToNode(index)} '\n",
|
||||
" plan_output += f'Time({solution.Value(time_var)}) -> '\n",
|
||||
" plan_output += f\"{manager.IndexToNode(index)} \"\n",
|
||||
" plan_output += f\"Time({solution.Value(time_var)}) -> \"\n",
|
||||
" index = solution.Value(routing.NextVar(index))\n",
|
||||
" time_var = time_dimension.CumulVar(index)\n",
|
||||
" plan_output += f'{manager.IndexToNode(index)} '\n",
|
||||
" plan_output += f'Time({solution.Value(time_var)})'\n",
|
||||
" plan_output += f\"{manager.IndexToNode(index)} \"\n",
|
||||
" plan_output += f\"Time({solution.Value(time_var)})\"\n",
|
||||
" print(plan_output)\n",
|
||||
" route_time = solution.Value(time_var) - start_time\n",
|
||||
" print(f'Time of the route: {route_time}min\\n')\n",
|
||||
" print(f\"Time of the route: {route_time}min\\n\")\n",
|
||||
" total_time += route_time\n",
|
||||
" print(f'Total time of all routes: {total_time}min')\n",
|
||||
" print(f\"Total time of all routes: {total_time}min\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -168,20 +172,20 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['time_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"time_matrix\"]), data[\"num_vehicles\"], data[\"depot\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" # Create and register a transit callback.\n",
|
||||
" def time_callback(from_index, to_index):\n",
|
||||
" \"\"\"Returns the travel time 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",
|
||||
" return data[\"time_matrix\"][from_node][to_node]\n",
|
||||
"\n",
|
||||
" transit_callback_index = routing.RegisterTransitCallback(time_callback)\n",
|
||||
"\n",
|
||||
@@ -189,13 +193,14 @@
|
||||
" routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)\n",
|
||||
"\n",
|
||||
" # Add Time Windows constraint.\n",
|
||||
" time = 'Time'\n",
|
||||
" time = \"Time\"\n",
|
||||
" routing.AddDimension(\n",
|
||||
" transit_callback_index,\n",
|
||||
" 10, # need optional waiting time to place break\n",
|
||||
" 180, # maximum time per vehicle\n",
|
||||
" False, # Don't force start cumul to zero.\n",
|
||||
" time)\n",
|
||||
" time,\n",
|
||||
" )\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(time)\n",
|
||||
" time_dimension.SetGlobalSpanCostCoefficient(10)\n",
|
||||
"\n",
|
||||
@@ -209,27 +214,30 @@
|
||||
" node_visit_transit = [0] * routing.Size()\n",
|
||||
" for index in range(routing.Size()):\n",
|
||||
" node = manager.IndexToNode(index)\n",
|
||||
" node_visit_transit[index] = data['service_time'][node]\n",
|
||||
" node_visit_transit[index] = data[\"service_time\"][node]\n",
|
||||
"\n",
|
||||
" # Add a break lasting 5 minutes, start between 25 and 45 minutes after route start\n",
|
||||
" for v in range(manager.GetNumberOfVehicles()):\n",
|
||||
" start_var = time_dimension.CumulVar(routing.Start(v))\n",
|
||||
" break_start = routing.solver().Sum(\n",
|
||||
" [routing.solver().IntVar(25, 45), start_var])\n",
|
||||
" break_start = routing.solver().Sum([routing.solver().IntVar(25, 45), start_var])\n",
|
||||
"\n",
|
||||
" break_intervals = [\n",
|
||||
" routing.solver().FixedDurationIntervalVar(break_start, 5,\n",
|
||||
" f'Break for vehicle {v}')\n",
|
||||
" routing.solver().FixedDurationIntervalVar(\n",
|
||||
" break_start, 5, f\"Break for vehicle {v}\"\n",
|
||||
" )\n",
|
||||
" ]\n",
|
||||
" time_dimension.SetBreakIntervalsOfVehicle(break_intervals, v,\n",
|
||||
" node_visit_transit)\n",
|
||||
" time_dimension.SetBreakIntervalsOfVehicle(\n",
|
||||
" break_intervals, v, node_visit_transit\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" # search_parameters.log_search = True\n",
|
||||
" search_parameters.time_limit.FromSeconds(2)\n",
|
||||
"\n",
|
||||
@@ -240,7 +248,7 @@
|
||||
" if solution:\n",
|
||||
" print_solution(manager, routing, solution)\n",
|
||||
" else:\n",
|
||||
" print('No solution found !')\n",
|
||||
" print(\"No solution found !\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -192,10 +192,10 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.time_limit.FromSeconds(1)\n",
|
||||
"\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -205,10 +205,10 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.time_limit.FromSeconds(1)\n",
|
||||
"\n",
|
||||
|
||||
@@ -75,12 +75,12 @@
|
||||
"\n",
|
||||
"Simple Vehicles Routing Problem (VRP).\n",
|
||||
"\n",
|
||||
" This is a sample using the routing library python wrapper to solve a VRP\n",
|
||||
" problem.\n",
|
||||
" A description of the problem can be found here:\n",
|
||||
" http://en.wikipedia.org/wiki/Vehicle_routing_problem.\n",
|
||||
"This is a sample using the routing library python wrapper to solve a VRP\n",
|
||||
"problem.\n",
|
||||
"A description of the problem can be found here:\n",
|
||||
"http://en.wikipedia.org/wiki/Vehicle_routing_problem.\n",
|
||||
"\n",
|
||||
" Distances are in meters.\n",
|
||||
"Distances are in meters.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
@@ -91,8 +91,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -146,7 +146,6 @@
|
||||
" print(f\"Maximum of the route distances: {max_route_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" \"\"\"Entry point of the program.\"\"\"\n",
|
||||
" # Instantiate the data problem.\n",
|
||||
@@ -188,7 +187,7 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -146,7 +146,6 @@
|
||||
" print(f\"Maximum of the route distances: {max_route_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" \"\"\"Solve the CVRP problem.\"\"\"\n",
|
||||
" # Instantiate the data problem.\n",
|
||||
@@ -188,10 +187,10 @@
|
||||
" # Close model with the custom search parameters.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.time_limit.FromSeconds(5)\n",
|
||||
" # When an initial solution is given for search, the model will be closed with\n",
|
||||
|
||||
@@ -73,9 +73,9 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Vehicles Routing Problem (VRP) for delivering items from any suppliers.\n",
|
||||
"Description:\n",
|
||||
"Need to deliver some item X and Y at end nodes (at least 11 X and 13 Y).\n",
|
||||
"Several locations provide them and even few provide both.\n",
|
||||
"\n",
|
||||
"Description: Need to deliver some item X and Y at end nodes (at least 11 X and\n",
|
||||
"13 Y). Several locations provide them and even few provide both.\n",
|
||||
"\n",
|
||||
"fleet:\n",
|
||||
" * vehicles: 2\n",
|
||||
@@ -93,22 +93,22 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" data['num_vehicles'] = 2\n",
|
||||
" data['starts'] = [0] * data['num_vehicles']\n",
|
||||
" data['ends'] = [1] * data['num_vehicles']\n",
|
||||
" assert len(data['starts']) == data['num_vehicles']\n",
|
||||
" assert len(data['ends']) == data['num_vehicles']\n",
|
||||
" data[\"num_vehicles\"] = 2\n",
|
||||
" data[\"starts\"] = [0] * data[\"num_vehicles\"]\n",
|
||||
" data[\"ends\"] = [1] * data[\"num_vehicles\"]\n",
|
||||
" assert len(data[\"starts\"]) == data[\"num_vehicles\"]\n",
|
||||
" assert len(data[\"ends\"]) == data[\"num_vehicles\"]\n",
|
||||
"\n",
|
||||
" # Need 11 X and 13 Y\n",
|
||||
" data['providers_x'] = [\n",
|
||||
" data[\"providers_x\"] = [\n",
|
||||
" 0, # start\n",
|
||||
" -11, # end\n",
|
||||
" 2, # X supply 1\n",
|
||||
@@ -127,7 +127,7 @@
|
||||
" 0, # Y supply 5\n",
|
||||
" 0, # Y supply 6\n",
|
||||
" ]\n",
|
||||
" data['providers_y'] = [\n",
|
||||
" data[\"providers_y\"] = [\n",
|
||||
" 0, # start\n",
|
||||
" -13, # ends\n",
|
||||
" 0, # X supply 1\n",
|
||||
@@ -146,95 +146,350 @@
|
||||
" 3, # Y supply 5\n",
|
||||
" 5, # Y supply 6\n",
|
||||
" ]\n",
|
||||
" data['vehicle_capacities_x'] = [15] * data['num_vehicles']\n",
|
||||
" data['vehicle_capacities_y'] = [15] * data['num_vehicles']\n",
|
||||
" assert len(data['vehicle_capacities_x']) == data['num_vehicles']\n",
|
||||
" assert len(data['vehicle_capacities_y']) == data['num_vehicles']\n",
|
||||
" data['distance_matrix'] = [\n",
|
||||
" data[\"vehicle_capacities_x\"] = [15] * data[\"num_vehicles\"]\n",
|
||||
" data[\"vehicle_capacities_y\"] = [15] * data[\"num_vehicles\"]\n",
|
||||
" assert len(data[\"vehicle_capacities_x\"]) == data[\"num_vehicles\"]\n",
|
||||
" assert len(data[\"vehicle_capacities_y\"]) == data[\"num_vehicles\"]\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" [\n",
|
||||
" 0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354,\n",
|
||||
" 468, 776, 662\n",
|
||||
" 0,\n",
|
||||
" 548,\n",
|
||||
" 776,\n",
|
||||
" 696,\n",
|
||||
" 582,\n",
|
||||
" 274,\n",
|
||||
" 502,\n",
|
||||
" 194,\n",
|
||||
" 308,\n",
|
||||
" 194,\n",
|
||||
" 536,\n",
|
||||
" 502,\n",
|
||||
" 388,\n",
|
||||
" 354,\n",
|
||||
" 468,\n",
|
||||
" 776,\n",
|
||||
" 662,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" 548,\n",
|
||||
" 0,\n",
|
||||
" 684,\n",
|
||||
" 308,\n",
|
||||
" 194,\n",
|
||||
" 502,\n",
|
||||
" 730,\n",
|
||||
" 354,\n",
|
||||
" 696,\n",
|
||||
" 742,\n",
|
||||
" 1084,\n",
|
||||
" 594,\n",
|
||||
" 480,\n",
|
||||
" 674,\n",
|
||||
" 1016,\n",
|
||||
" 868,\n",
|
||||
" 1210,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" 776,\n",
|
||||
" 684,\n",
|
||||
" 0,\n",
|
||||
" 992,\n",
|
||||
" 878,\n",
|
||||
" 502,\n",
|
||||
" 274,\n",
|
||||
" 810,\n",
|
||||
" 468,\n",
|
||||
" 742,\n",
|
||||
" 400,\n",
|
||||
" 1278,\n",
|
||||
" 1164,\n",
|
||||
" 1130,\n",
|
||||
" 788,\n",
|
||||
" 1552,\n",
|
||||
" 754,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" 696,\n",
|
||||
" 308,\n",
|
||||
" 992,\n",
|
||||
" 0,\n",
|
||||
" 114,\n",
|
||||
" 650,\n",
|
||||
" 878,\n",
|
||||
" 502,\n",
|
||||
" 844,\n",
|
||||
" 890,\n",
|
||||
" 1232,\n",
|
||||
" 514,\n",
|
||||
" 628,\n",
|
||||
" 822,\n",
|
||||
" 1164,\n",
|
||||
" 560,\n",
|
||||
" 1358,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" 582,\n",
|
||||
" 194,\n",
|
||||
" 878,\n",
|
||||
" 114,\n",
|
||||
" 0,\n",
|
||||
" 536,\n",
|
||||
" 764,\n",
|
||||
" 388,\n",
|
||||
" 730,\n",
|
||||
" 776,\n",
|
||||
" 1118,\n",
|
||||
" 400,\n",
|
||||
" 514,\n",
|
||||
" 708,\n",
|
||||
" 1050,\n",
|
||||
" 674,\n",
|
||||
" 1244,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" 274,\n",
|
||||
" 502,\n",
|
||||
" 502,\n",
|
||||
" 650,\n",
|
||||
" 536,\n",
|
||||
" 0,\n",
|
||||
" 228,\n",
|
||||
" 308,\n",
|
||||
" 194,\n",
|
||||
" 240,\n",
|
||||
" 582,\n",
|
||||
" 776,\n",
|
||||
" 662,\n",
|
||||
" 628,\n",
|
||||
" 514,\n",
|
||||
" 1050,\n",
|
||||
" 708,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" 502,\n",
|
||||
" 730,\n",
|
||||
" 274,\n",
|
||||
" 878,\n",
|
||||
" 764,\n",
|
||||
" 228,\n",
|
||||
" 0,\n",
|
||||
" 536,\n",
|
||||
" 194,\n",
|
||||
" 468,\n",
|
||||
" 354,\n",
|
||||
" 1004,\n",
|
||||
" 890,\n",
|
||||
" 856,\n",
|
||||
" 514,\n",
|
||||
" 1278,\n",
|
||||
" 480,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" 194,\n",
|
||||
" 354,\n",
|
||||
" 810,\n",
|
||||
" 502,\n",
|
||||
" 388,\n",
|
||||
" 308,\n",
|
||||
" 536,\n",
|
||||
" 0,\n",
|
||||
" 342,\n",
|
||||
" 388,\n",
|
||||
" 730,\n",
|
||||
" 468,\n",
|
||||
" 354,\n",
|
||||
" 320,\n",
|
||||
" 662,\n",
|
||||
" 742,\n",
|
||||
" 856,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" 308,\n",
|
||||
" 696,\n",
|
||||
" 468,\n",
|
||||
" 844,\n",
|
||||
" 730,\n",
|
||||
" 194,\n",
|
||||
" 194,\n",
|
||||
" 342,\n",
|
||||
" 0,\n",
|
||||
" 274,\n",
|
||||
" 388,\n",
|
||||
" 810,\n",
|
||||
" 696,\n",
|
||||
" 662,\n",
|
||||
" 320,\n",
|
||||
" 1084,\n",
|
||||
" 514,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" 194,\n",
|
||||
" 742,\n",
|
||||
" 742,\n",
|
||||
" 890,\n",
|
||||
" 776,\n",
|
||||
" 240,\n",
|
||||
" 468,\n",
|
||||
" 388,\n",
|
||||
" 274,\n",
|
||||
" 0,\n",
|
||||
" 342,\n",
|
||||
" 536,\n",
|
||||
" 422,\n",
|
||||
" 388,\n",
|
||||
" 274,\n",
|
||||
" 810,\n",
|
||||
" 468,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" 536,\n",
|
||||
" 1084,\n",
|
||||
" 400,\n",
|
||||
" 1232,\n",
|
||||
" 1118,\n",
|
||||
" 582,\n",
|
||||
" 354,\n",
|
||||
" 730,\n",
|
||||
" 388,\n",
|
||||
" 342,\n",
|
||||
" 0,\n",
|
||||
" 878,\n",
|
||||
" 764,\n",
|
||||
" 730,\n",
|
||||
" 388,\n",
|
||||
" 1152,\n",
|
||||
" 354,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" 502,\n",
|
||||
" 594,\n",
|
||||
" 1278,\n",
|
||||
" 514,\n",
|
||||
" 400,\n",
|
||||
" 776,\n",
|
||||
" 1004,\n",
|
||||
" 468,\n",
|
||||
" 810,\n",
|
||||
" 536,\n",
|
||||
" 878,\n",
|
||||
" 0,\n",
|
||||
" 114,\n",
|
||||
" 308,\n",
|
||||
" 650,\n",
|
||||
" 274,\n",
|
||||
" 844,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" 388,\n",
|
||||
" 480,\n",
|
||||
" 1164,\n",
|
||||
" 628,\n",
|
||||
" 514,\n",
|
||||
" 662,\n",
|
||||
" 890,\n",
|
||||
" 354,\n",
|
||||
" 696,\n",
|
||||
" 422,\n",
|
||||
" 764,\n",
|
||||
" 114,\n",
|
||||
" 0,\n",
|
||||
" 194,\n",
|
||||
" 536,\n",
|
||||
" 388,\n",
|
||||
" 730,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" 354,\n",
|
||||
" 674,\n",
|
||||
" 1130,\n",
|
||||
" 822,\n",
|
||||
" 708,\n",
|
||||
" 628,\n",
|
||||
" 856,\n",
|
||||
" 320,\n",
|
||||
" 662,\n",
|
||||
" 388,\n",
|
||||
" 730,\n",
|
||||
" 308,\n",
|
||||
" 194,\n",
|
||||
" 0,\n",
|
||||
" 342,\n",
|
||||
" 422,\n",
|
||||
" 536,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" 468,\n",
|
||||
" 1016,\n",
|
||||
" 788,\n",
|
||||
" 1164,\n",
|
||||
" 1050,\n",
|
||||
" 514,\n",
|
||||
" 514,\n",
|
||||
" 662,\n",
|
||||
" 320,\n",
|
||||
" 274,\n",
|
||||
" 388,\n",
|
||||
" 650,\n",
|
||||
" 536,\n",
|
||||
" 342,\n",
|
||||
" 0,\n",
|
||||
" 764,\n",
|
||||
" 194,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" 776,\n",
|
||||
" 868,\n",
|
||||
" 1552,\n",
|
||||
" 560,\n",
|
||||
" 674,\n",
|
||||
" 1050,\n",
|
||||
" 1278,\n",
|
||||
" 742,\n",
|
||||
" 1084,\n",
|
||||
" 810,\n",
|
||||
" 1152,\n",
|
||||
" 274,\n",
|
||||
" 388,\n",
|
||||
" 422,\n",
|
||||
" 764,\n",
|
||||
" 0,\n",
|
||||
" 798,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" 662,\n",
|
||||
" 1210,\n",
|
||||
" 754,\n",
|
||||
" 1358,\n",
|
||||
" 1244,\n",
|
||||
" 708,\n",
|
||||
" 480,\n",
|
||||
" 856,\n",
|
||||
" 514,\n",
|
||||
" 468,\n",
|
||||
" 354,\n",
|
||||
" 844,\n",
|
||||
" 730,\n",
|
||||
" 536,\n",
|
||||
" 194,\n",
|
||||
" 798,\n",
|
||||
" 0,\n",
|
||||
" ],\n",
|
||||
" ]\n",
|
||||
" assert len(data['providers_x']) == len(data['distance_matrix'])\n",
|
||||
" assert len(data['providers_y']) == len(data['distance_matrix'])\n",
|
||||
" assert len(data[\"providers_x\"]) == len(data[\"distance_matrix\"])\n",
|
||||
" assert len(data[\"providers_y\"]) == len(data[\"distance_matrix\"])\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(data, manager, routing, assignment):\n",
|
||||
" \"\"\"Prints assignment on console.\"\"\"\n",
|
||||
" print(f'Objective: {assignment.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {assignment.ObjectiveValue()}\")\n",
|
||||
" # Display dropped nodes.\n",
|
||||
" dropped_nodes = 'Dropped nodes:'\n",
|
||||
" dropped_nodes = \"Dropped nodes:\"\n",
|
||||
" for node in range(routing.Size()):\n",
|
||||
" if routing.IsStart(node) or routing.IsEnd(node):\n",
|
||||
" continue\n",
|
||||
" if assignment.Value(routing.NextVar(node)) == node:\n",
|
||||
" dropped_nodes += f' {manager.IndexToNode(node)}'\n",
|
||||
" dropped_nodes += f\" {manager.IndexToNode(node)}\"\n",
|
||||
" print(dropped_nodes)\n",
|
||||
" # Display routes\n",
|
||||
" total_distance = 0\n",
|
||||
@@ -242,31 +497,31 @@
|
||||
" total_load_y = 0\n",
|
||||
" for vehicle_id in range(manager.GetNumberOfVehicles()):\n",
|
||||
" index = routing.Start(vehicle_id)\n",
|
||||
" plan_output = f'Route for vehicle {vehicle_id}:\\n'\n",
|
||||
" plan_output = f\"Route for vehicle {vehicle_id}:\\n\"\n",
|
||||
" route_distance = 0\n",
|
||||
" route_load_x = 0\n",
|
||||
" route_load_y = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" node_index = manager.IndexToNode(index)\n",
|
||||
" route_load_x += data['providers_x'][node_index]\n",
|
||||
" route_load_y += data['providers_y'][node_index]\n",
|
||||
" plan_output += f' {node_index} Load(X:{route_load_x}, Y:{route_load_y}) -> '\n",
|
||||
" route_load_x += data[\"providers_x\"][node_index]\n",
|
||||
" route_load_y += data[\"providers_y\"][node_index]\n",
|
||||
" plan_output += f\" {node_index} Load(X:{route_load_x}, Y:{route_load_y}) -> \"\n",
|
||||
" previous_index = index\n",
|
||||
" previous_node_index = node_index\n",
|
||||
" index = assignment.Value(routing.NextVar(index))\n",
|
||||
" node_index = manager.IndexToNode(index)\n",
|
||||
" #route_distance += routing.GetArcCostForVehicle(previous_index, index, vehicle_id)\n",
|
||||
" route_distance += data['distance_matrix'][previous_node_index][node_index]\n",
|
||||
" # route_distance += routing.GetArcCostForVehicle(previous_index, index, vehicle_id)\n",
|
||||
" route_distance += data[\"distance_matrix\"][previous_node_index][node_index]\n",
|
||||
" node_index = manager.IndexToNode(index)\n",
|
||||
" plan_output += f' {node_index} Load({route_load_x}, {route_load_y})\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" plan_output += f'Load of the route: X:{route_load_x}, Y:{route_load_y}\\n'\n",
|
||||
" plan_output += f\" {node_index} Load({route_load_x}, {route_load_y})\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" plan_output += f\"Load of the route: X:{route_load_x}, Y:{route_load_y}\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += route_distance\n",
|
||||
" total_load_x += route_load_x\n",
|
||||
" total_load_y += route_load_y\n",
|
||||
" print(f'Total Distance of all routes: {total_distance}m')\n",
|
||||
" print(f'Total load of all routes: X:{total_load_x}, Y:{total_load_y}')\n",
|
||||
" print(f\"Total Distance of all routes: {total_distance}m\")\n",
|
||||
" print(f\"Total load of all routes: X:{total_load_x}, Y:{total_load_y}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -275,9 +530,12 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'], data['starts'],\n",
|
||||
" data['ends'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_matrix\"]),\n",
|
||||
" data[\"num_vehicles\"],\n",
|
||||
" data[\"starts\"],\n",
|
||||
" data[\"ends\"],\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
@@ -289,7 +547,7 @@
|
||||
" # Convert from routing variable Index to distance matrix NodeIndex.\n",
|
||||
" from_node = manager.IndexToNode(from_index)\n",
|
||||
" to_node = manager.IndexToNode(to_index)\n",
|
||||
" return data['distance_matrix'][from_node][to_node]\n",
|
||||
" return data[\"distance_matrix\"][from_node][to_node]\n",
|
||||
"\n",
|
||||
" transit_callback_index = routing.RegisterTransitCallback(distance_callback)\n",
|
||||
"\n",
|
||||
@@ -297,13 +555,14 @@
|
||||
" routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)\n",
|
||||
"\n",
|
||||
" # Add Distance constraint.\n",
|
||||
" dimension_name = 'Distance'\n",
|
||||
" dimension_name = \"Distance\"\n",
|
||||
" routing.AddDimension(\n",
|
||||
" transit_callback_index,\n",
|
||||
" 0, # no slack\n",
|
||||
" 2000, # vehicle maximum travel distance\n",
|
||||
" True, # start cumul to zero\n",
|
||||
" dimension_name)\n",
|
||||
" dimension_name,\n",
|
||||
" )\n",
|
||||
" distance_dimension = routing.GetDimensionOrDie(dimension_name)\n",
|
||||
" # Minimize the longest road\n",
|
||||
" distance_dimension.SetGlobalSpanCostCoefficient(100)\n",
|
||||
@@ -314,63 +573,67 @@
|
||||
" \"\"\"Returns the demand of the node.\"\"\"\n",
|
||||
" # Convert from routing variable Index to demands NodeIndex.\n",
|
||||
" from_node = manager.IndexToNode(from_index)\n",
|
||||
" return data['providers_x'][from_node]\n",
|
||||
" return data[\"providers_x\"][from_node]\n",
|
||||
"\n",
|
||||
" demand_callback_x_index = routing.RegisterUnaryTransitCallback(\n",
|
||||
" demand_callback_x)\n",
|
||||
" demand_callback_x_index = routing.RegisterUnaryTransitCallback(demand_callback_x)\n",
|
||||
" routing.AddDimensionWithVehicleCapacity(\n",
|
||||
" demand_callback_x_index,\n",
|
||||
" 0, # null capacity slack\n",
|
||||
" data['vehicle_capacities_x'], # vehicle maximum capacities\n",
|
||||
" data[\"vehicle_capacities_x\"], # vehicle maximum capacities\n",
|
||||
" True, # start cumul to zero\n",
|
||||
" 'Load_x')\n",
|
||||
" \"Load_x\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" def demand_callback_y(from_index):\n",
|
||||
" \"\"\"Returns the demand of the node.\"\"\"\n",
|
||||
" # Convert from routing variable Index to demands NodeIndex.\n",
|
||||
" from_node = manager.IndexToNode(from_index)\n",
|
||||
" return data['providers_y'][from_node]\n",
|
||||
" return data[\"providers_y\"][from_node]\n",
|
||||
"\n",
|
||||
" demand_callback_y_index = routing.RegisterUnaryTransitCallback(\n",
|
||||
" demand_callback_y)\n",
|
||||
" demand_callback_y_index = routing.RegisterUnaryTransitCallback(demand_callback_y)\n",
|
||||
" routing.AddDimensionWithVehicleCapacity(\n",
|
||||
" demand_callback_y_index,\n",
|
||||
" 0, # null capacity slack\n",
|
||||
" data['vehicle_capacities_y'], # vehicle maximum capacities\n",
|
||||
" data[\"vehicle_capacities_y\"], # vehicle maximum capacities\n",
|
||||
" True, # start cumul to zero\n",
|
||||
" 'Load_y')\n",
|
||||
" \"Load_y\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Add constraint at end\n",
|
||||
" solver = routing.solver()\n",
|
||||
" load_x_dim = routing.GetDimensionOrDie('Load_x')\n",
|
||||
" load_y_dim = routing.GetDimensionOrDie('Load_y')\n",
|
||||
" load_x_dim = routing.GetDimensionOrDie(\"Load_x\")\n",
|
||||
" load_y_dim = routing.GetDimensionOrDie(\"Load_y\")\n",
|
||||
" ends = []\n",
|
||||
" for v in range(manager.GetNumberOfVehicles()):\n",
|
||||
" ends.append(routing.End(v))\n",
|
||||
"\n",
|
||||
" node_end = data['ends'][0]\n",
|
||||
" node_end = data[\"ends\"][0]\n",
|
||||
" solver.Add(\n",
|
||||
" solver.Sum([load_x_dim.CumulVar(l)\n",
|
||||
" for l in ends]) >= -data['providers_x'][node_end])\n",
|
||||
" solver.Sum([load_x_dim.CumulVar(l) for l in ends])\n",
|
||||
" >= -data[\"providers_x\"][node_end]\n",
|
||||
" )\n",
|
||||
" solver.Add(\n",
|
||||
" solver.Sum([load_y_dim.CumulVar(l)\n",
|
||||
" for l in ends]) >= -data['providers_y'][node_end])\n",
|
||||
" #solver.Add(load_y_dim.CumulVar(end) >= -data['providers_y'][node_end])\n",
|
||||
" solver.Sum([load_y_dim.CumulVar(l) for l in ends])\n",
|
||||
" >= -data[\"providers_y\"][node_end]\n",
|
||||
" )\n",
|
||||
" # solver.Add(load_y_dim.CumulVar(end) >= -data['providers_y'][node_end])\n",
|
||||
"\n",
|
||||
" # Allow to freely drop any nodes.\n",
|
||||
" penalty = 0\n",
|
||||
" for node in range(0, len(data['distance_matrix'])):\n",
|
||||
" if node not in data['starts'] and node not in data['ends']:\n",
|
||||
" for node in range(0, len(data[\"distance_matrix\"])):\n",
|
||||
" if node not in data[\"starts\"] and node not in data[\"ends\"]:\n",
|
||||
" routing.AddDisjunction([manager.NodeToIndex(node)], penalty)\n",
|
||||
"\n",
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" # Sets a time limit; default is 100 milliseconds.\n",
|
||||
" #search_parameters.log_search = True\n",
|
||||
" # search_parameters.log_search = True\n",
|
||||
" search_parameters.time_limit.FromSeconds(1)\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
@@ -380,7 +643,7 @@
|
||||
" if solution:\n",
|
||||
" print_solution(data, manager, routing, solution)\n",
|
||||
" else:\n",
|
||||
" print('no solution found !')\n",
|
||||
" print(\"no solution found !\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -87,8 +87,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -140,7 +140,6 @@
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(data, manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
@@ -181,7 +180,6 @@
|
||||
" print(f\"Maximum of the route distances: {max_route_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" \"\"\"Solve the CVRP problem.\"\"\"\n",
|
||||
" # Instantiate the data problem.\n",
|
||||
@@ -195,7 +193,6 @@
|
||||
" # Create Routing Model.\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" # Create and register a transit callback.\n",
|
||||
" def distance_callback(from_index, to_index):\n",
|
||||
" \"\"\"Returns the distance between the two nodes.\"\"\"\n",
|
||||
@@ -292,10 +289,10 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" # search_parameters.log_search = True\n",
|
||||
" search_parameters.time_limit.FromSeconds(5)\n",
|
||||
|
||||
@@ -78,11 +78,15 @@
|
||||
"the relation between nodes and indices.\n",
|
||||
"\n",
|
||||
"Things to notice:\n",
|
||||
"* Since we have two duplicates (node 5 and node 4) solver need 2 extra indices to have an unique index for each vehicle start/stop and locations.\n",
|
||||
"* Solver needs to \"create\" an index for a vehicle 1 start since solver need an unique start index per vehicle.\n",
|
||||
"* Since we have two duplicates (node 5 and node 4) solver need 2 extra indices\n",
|
||||
"to have an unique index for each vehicle start/stop and locations.\n",
|
||||
"* Solver needs to \"create\" an index for a vehicle 1 start since solver need an\n",
|
||||
"unique start index per vehicle.\n",
|
||||
"* All end nodes are moved to the end of the index list aka [15, 16, 17, 18].\n",
|
||||
"* routing.Size() return the number of node which are not end nodes (here 15 aka [0-14])\n",
|
||||
"note: using the two properties above, we know that any index in range(routing.Size()) is not a vehicle end node.\n",
|
||||
"* routing.Size() return the number of node which are not end nodes (here 15 aka\n",
|
||||
"[0-14])\n",
|
||||
"note: using the two properties above, we know that any index in\n",
|
||||
"range(routing.Size()) is not a vehicle end node.\n",
|
||||
"\n",
|
||||
"* Since end nodes are moved to the end, their respective \"empty\" node index are\n",
|
||||
"reused so all locations indices are \"shifted\"\n",
|
||||
@@ -91,9 +95,11 @@
|
||||
"e.g. start node 7 mapped to index 4\n",
|
||||
"\n",
|
||||
"Takeaway:\n",
|
||||
"* Allways use routing.Start(), routing.End(), manager.IndexToNode() or manager.NodeToIndex().\n",
|
||||
"* Allways use routing.Start(), routing.End(), manager.IndexToNode() or\n",
|
||||
"manager.NodeToIndex().\n",
|
||||
"* Location node is not necessarily equal to its index.\n",
|
||||
"* To loop through ALL indices use manager.GetNumberOfIndices() (Python) or manager::num_indices() (C++)\n",
|
||||
"* To loop through ALL indices use manager.GetNumberOfIndices() (Python) or\n",
|
||||
"manager::num_indices() (C++)\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
@@ -104,7 +110,6 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -119,57 +124,61 @@
|
||||
" manager = pywrapcp.RoutingIndexManager(locations, vehicles, starts, ends)\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
"\n",
|
||||
" print('Starts/Ends:')\n",
|
||||
" header = '| |'\n",
|
||||
" separator = '|---|'\n",
|
||||
" v_starts = '| start |'\n",
|
||||
" v_ends = '| end |'\n",
|
||||
" print(\"Starts/Ends:\")\n",
|
||||
" header = \"| |\"\n",
|
||||
" separator = \"|---|\"\n",
|
||||
" v_starts = \"| start |\"\n",
|
||||
" v_ends = \"| end |\"\n",
|
||||
" for v in range(manager.GetNumberOfVehicles()):\n",
|
||||
" header += f' vehicle {v} |'\n",
|
||||
" separator += '---|'\n",
|
||||
" v_starts += f' {starts[v]} |'\n",
|
||||
" v_ends += f' {ends[v]} |'\n",
|
||||
" header += f\" vehicle {v} |\"\n",
|
||||
" separator += \"---|\"\n",
|
||||
" v_starts += f\" {starts[v]} |\"\n",
|
||||
" v_ends += f\" {ends[v]} |\"\n",
|
||||
" print(header)\n",
|
||||
" print(separator)\n",
|
||||
" print(v_starts)\n",
|
||||
" print(v_ends)\n",
|
||||
"\n",
|
||||
" print('\\nNodes:')\n",
|
||||
" print(\"\\nNodes:\")\n",
|
||||
" print(\n",
|
||||
" '| locations | manager.GetNumberOfNodes | manager.GetNumberOfIndices | routing.nodes | routing.Size |'\n",
|
||||
" \"| locations | manager.GetNumberOfNodes | manager.GetNumberOfIndices |\"\n",
|
||||
" \" routing.nodes | routing.Size |\"\n",
|
||||
" )\n",
|
||||
" print('|---|---|---|---|---|')\n",
|
||||
" print(\"|---|---|---|---|---|\")\n",
|
||||
" print(\n",
|
||||
" f'| {locations} | {manager.GetNumberOfNodes()} | {manager.GetNumberOfIndices()} | {routing.nodes()} | {routing.Size()} |'\n",
|
||||
" f\"| {locations} | {manager.GetNumberOfNodes()} |\"\n",
|
||||
" f\" {manager.GetNumberOfIndices()} | {routing.nodes()} |\"\n",
|
||||
" f\" {routing.Size()} |\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" print('\\nLocations:')\n",
|
||||
" print('| node | index | routing.IsStart | routing.IsEnd |')\n",
|
||||
" print('|---|---|---|---|')\n",
|
||||
" print(\"\\nLocations:\")\n",
|
||||
" print(\"| node | index | routing.IsStart | routing.IsEnd |\")\n",
|
||||
" print(\"|---|---|---|---|\")\n",
|
||||
" for node in range(manager.GetNumberOfNodes()):\n",
|
||||
" if node in starts or node in ends:\n",
|
||||
" continue\n",
|
||||
" index = manager.NodeToIndex(node)\n",
|
||||
" print(\n",
|
||||
" f'| {node} | {index} | {routing.IsStart(index)} | {routing.IsEnd(index)} |'\n",
|
||||
" f\"| {node} | {index} | {routing.IsStart(index)} |\"\n",
|
||||
" f\" {routing.IsEnd(index)} |\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" print('\\nStart/End:')\n",
|
||||
" print(\n",
|
||||
" '| vehicle | Start/end | node | index | routing.IsStart | routing.IsEnd |'\n",
|
||||
" )\n",
|
||||
" print('|---|---|---|---|---|---|')\n",
|
||||
" print(\"\\nStart/End:\")\n",
|
||||
" print(\"| vehicle | Start/end | node | index | routing.IsStart | routing.IsEnd |\")\n",
|
||||
" print(\"|---|---|---|---|---|---|\")\n",
|
||||
" for v in range(manager.GetNumberOfVehicles()):\n",
|
||||
" start_index = routing.Start(v)\n",
|
||||
" start_node = manager.IndexToNode(start_index)\n",
|
||||
" print(\n",
|
||||
" f'| {v} | start | {start_node} | {start_index} | {routing.IsStart(start_index)} | {routing.IsEnd(start_index)} |'\n",
|
||||
" f\"| {v} | start | {start_node} | {start_index} |\"\n",
|
||||
" f\" {routing.IsStart(start_index)} | {routing.IsEnd(start_index)} |\"\n",
|
||||
" )\n",
|
||||
" for v in range(manager.GetNumberOfVehicles()):\n",
|
||||
" end_index = routing.End(v)\n",
|
||||
" end_node = manager.IndexToNode(end_index)\n",
|
||||
" print(\n",
|
||||
" f'| {v} | end | {end_node} | {end_index} | {routing.IsStart(end_index)} | {routing.IsEnd(end_index)} |'\n",
|
||||
" f\"| {v} | end | {end_node} | {end_index} |\"\n",
|
||||
" f\" {routing.IsStart(end_index)} | {routing.IsEnd(end_index)} |\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -201,7 +201,7 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -204,7 +204,7 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -204,7 +204,7 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -248,7 +248,7 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -75,12 +75,12 @@
|
||||
"\n",
|
||||
"Simple Vehicles Routing Problem (VRP).\n",
|
||||
"\n",
|
||||
" This is a sample using the routing library python wrapper to solve a VRP\n",
|
||||
" problem.\n",
|
||||
"This is a sample using the routing library python wrapper to solve a VRP\n",
|
||||
"problem.\n",
|
||||
"\n",
|
||||
" The solver stop after improving its solution 15 times or after 5 seconds.\n",
|
||||
"The solver stop after improving its solution 15 times or after 5 seconds.\n",
|
||||
"\n",
|
||||
" Distances are in meters.\n",
|
||||
"Distances are in meters.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
@@ -93,8 +93,8 @@
|
||||
"source": [
|
||||
"import weakref\n",
|
||||
"\n",
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -127,7 +127,8 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(\n",
|
||||
" routing_manager: pywrapcp.RoutingIndexManager, routing_model: pywrapcp.RoutingModel\n",
|
||||
" routing_manager: pywrapcp.RoutingIndexManager,\n",
|
||||
" routing_model: pywrapcp.RoutingModel,\n",
|
||||
"):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(\"################\")\n",
|
||||
@@ -151,7 +152,6 @@
|
||||
" print(f\"Total Distance of all routes: {total_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class SolutionCallback:\n",
|
||||
" \"\"\"Create a solution callback.\"\"\"\n",
|
||||
"\n",
|
||||
@@ -169,14 +169,17 @@
|
||||
" self.objectives = []\n",
|
||||
"\n",
|
||||
" def __call__(self):\n",
|
||||
" objective = int(self._routing_model_ref().CostVar().Value())\n",
|
||||
" objective = int(\n",
|
||||
" self._routing_model_ref().CostVar().Value()\n",
|
||||
" ) # pytype: disable=attribute-error\n",
|
||||
" if not self.objectives or objective < self.objectives[-1]:\n",
|
||||
" self.objectives.append(objective)\n",
|
||||
" print_solution(self._routing_manager_ref(), self._routing_model_ref())\n",
|
||||
" print_solution(\n",
|
||||
" self._routing_manager_ref(), self._routing_model_ref()\n",
|
||||
" ) # pytype: disable=attribute-error\n",
|
||||
" self._counter += 1\n",
|
||||
" if self._counter > self._counter_limit:\n",
|
||||
" self._routing_model_ref().solver().FinishCurrentSearch()\n",
|
||||
"\n",
|
||||
" self._routing_model_ref().solver().FinishCurrentSearch() # pytype: disable=attribute-error\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -225,10 +228,10 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.time_limit.FromSeconds(5)\n",
|
||||
"\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -146,7 +146,10 @@
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_matrix\"]), data[\"num_vehicles\"], data[\"starts\"], data[\"ends\"]\n",
|
||||
" len(data[\"distance_matrix\"]),\n",
|
||||
" data[\"num_vehicles\"],\n",
|
||||
" data[\"starts\"],\n",
|
||||
" data[\"ends\"],\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
@@ -180,7 +183,7 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -220,7 +220,7 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -74,19 +74,19 @@
|
||||
"source": [
|
||||
"Vehicles Routing Problem (VRP) with Time Window (TW) per vehicle.\n",
|
||||
"\n",
|
||||
" All time are in minutes using 0am as origin\n",
|
||||
" e.g. 8am = 480, 11am = 660, 1pm = 780 ...\n",
|
||||
"All time are in minutes using 0am as origin\n",
|
||||
"e.g. 8am = 480, 11am = 660, 1pm = 780 ...\n",
|
||||
"\n",
|
||||
" We have 1 depot (0) and 16 locations (1-16).\n",
|
||||
" We have a fleet of 4 vehicles (0-3) whose working time is [480, 1020] (8am-5pm)\n",
|
||||
" We have the distance matrix between these locations and depot.\n",
|
||||
" We have a service time of 25min at each location.\n",
|
||||
"We have 1 depot (0) and 16 locations (1-16).\n",
|
||||
"We have a fleet of 4 vehicles (0-3) whose working time is [480, 1020] (8am-5pm)\n",
|
||||
"We have the distance matrix between these locations and depot.\n",
|
||||
"We have a service time of 25min at each location.\n",
|
||||
"\n",
|
||||
" Locations are duplicated so we can simulate a TW per vehicle.\n",
|
||||
" location: [01-16] vehicle: 0 TW: [540, 660] (9am-11am)\n",
|
||||
" location: [17-32] vehicle: 1 TW: [660, 780] (11am-1pm)\n",
|
||||
" location: [33-48] vehicle: 2 TW: [780, 900] (1pm-3pm)\n",
|
||||
" location: [49-64] vehicle: 3 TW: [900, 1020] (3pm-5pm)\n",
|
||||
"Locations are duplicated so we can simulate a TW per vehicle.\n",
|
||||
"location: [01-16] vehicle: 0 TW: [540, 660] (9am-11am)\n",
|
||||
"location: [17-32] vehicle: 1 TW: [660, 780] (11am-1pm)\n",
|
||||
"location: [33-48] vehicle: 2 TW: [780, 900] (1pm-3pm)\n",
|
||||
"location: [49-64] vehicle: 3 TW: [900, 1020] (3pm-5pm)\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
@@ -97,14 +97,15 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" data['time_matrix'] = [\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",
|
||||
@@ -123,16 +124,16 @@
|
||||
" [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['num_vehicles'] = 4\n",
|
||||
" data['depot'] = 0\n",
|
||||
" data[\"num_vehicles\"] = 4\n",
|
||||
" data[\"depot\"] = 0\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(manager, routing, assignment):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {assignment.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {assignment.ObjectiveValue()}\")\n",
|
||||
" # Display dropped nodes.\n",
|
||||
" dropped_nodes = 'Dropped nodes:'\n",
|
||||
" dropped_nodes = \"Dropped nodes:\"\n",
|
||||
" for index in range(routing.Size()):\n",
|
||||
" if routing.IsStart(index) or routing.IsEnd(index):\n",
|
||||
" continue\n",
|
||||
@@ -142,15 +143,15 @@
|
||||
" original = node\n",
|
||||
" while original > 16:\n",
|
||||
" original = original - 16\n",
|
||||
" dropped_nodes += f' {node}({original})'\n",
|
||||
" dropped_nodes += f\" {node}({original})\"\n",
|
||||
" else:\n",
|
||||
" dropped_nodes += f' {node}'\n",
|
||||
" dropped_nodes += f\" {node}\"\n",
|
||||
" print(dropped_nodes)\n",
|
||||
" # Display routes\n",
|
||||
" time_dimension = routing.GetDimensionOrDie('Time')\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(\"Time\")\n",
|
||||
" total_time = 0\n",
|
||||
" for vehicle_id in range(manager.GetNumberOfVehicles()):\n",
|
||||
" plan_output = f'Route for vehicle {vehicle_id}:\\n'\n",
|
||||
" plan_output = f\"Route for vehicle {vehicle_id}:\\n\"\n",
|
||||
" index = routing.Start(vehicle_id)\n",
|
||||
" start_time = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
@@ -160,22 +161,22 @@
|
||||
" original = node\n",
|
||||
" while original > 16:\n",
|
||||
" original = original - 16\n",
|
||||
" plan_output += f'{node}({original})'\n",
|
||||
" plan_output += f\"{node}({original})\"\n",
|
||||
" else:\n",
|
||||
" plan_output += f'{node}'\n",
|
||||
" plan_output += f' Time:{assignment.Value(time_var)} -> '\n",
|
||||
" plan_output += f\"{node}\"\n",
|
||||
" plan_output += f\" Time:{assignment.Value(time_var)} -> \"\n",
|
||||
" if start_time == 0:\n",
|
||||
" start_time = assignment.Value(time_var)\n",
|
||||
" index = assignment.Value(routing.NextVar(index))\n",
|
||||
" time_var = time_dimension.CumulVar(index)\n",
|
||||
" node = manager.IndexToNode(index)\n",
|
||||
" plan_output += f'{node} Time:{assignment.Value(time_var)}\\n'\n",
|
||||
" plan_output += f\"{node} Time:{assignment.Value(time_var)}\\n\"\n",
|
||||
" end_time = assignment.Value(time_var)\n",
|
||||
" duration = end_time - start_time\n",
|
||||
" plan_output += f'Duration of the route:{duration}min\\n'\n",
|
||||
" plan_output += f\"Duration of the route:{duration}min\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_time += duration\n",
|
||||
" print(f'Total duration of all routes: {total_time}min')\n",
|
||||
" print(f\"Total duration of all routes: {total_time}min\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -185,9 +186,8 @@
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" 1 + 16 * 4, # number of locations\n",
|
||||
" data['num_vehicles'],\n",
|
||||
" data['depot'])\n",
|
||||
" 1 + 16 * 4, data[\"num_vehicles\"], data[\"depot\"] # number of locations\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
@@ -207,9 +207,9 @@
|
||||
" to_node = to_node - 16\n",
|
||||
" # add service of 25min for each location (except depot)\n",
|
||||
" service_time = 0\n",
|
||||
" if from_node != data['depot']:\n",
|
||||
" if from_node != data[\"depot\"]:\n",
|
||||
" service_time = 25\n",
|
||||
" return data['time_matrix'][from_node][to_node] + service_time\n",
|
||||
" return data[\"time_matrix\"][from_node][to_node] + service_time\n",
|
||||
"\n",
|
||||
" transit_callback_index = routing.RegisterTransitCallback(time_callback)\n",
|
||||
"\n",
|
||||
@@ -217,17 +217,18 @@
|
||||
" routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)\n",
|
||||
"\n",
|
||||
" # Add Time Windows constraint.\n",
|
||||
" time = 'Time'\n",
|
||||
" time = \"Time\"\n",
|
||||
" routing.AddDimension(\n",
|
||||
" transit_callback_index,\n",
|
||||
" 0, # allow waiting time (0 min)\n",
|
||||
" 1020, # maximum time per vehicle (9 hours)\n",
|
||||
" False, # Don't force start cumul to zero.\n",
|
||||
" time)\n",
|
||||
" time,\n",
|
||||
" )\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(time)\n",
|
||||
" # Add time window constraints for each location except depot.\n",
|
||||
" for location_idx in range(17):\n",
|
||||
" if location_idx == data['depot']:\n",
|
||||
" if location_idx == data[\"depot\"]:\n",
|
||||
" continue\n",
|
||||
" # Vehicle 0 location TW: [9am, 11am]\n",
|
||||
" index_0 = manager.NodeToIndex(location_idx)\n",
|
||||
@@ -254,30 +255,32 @@
|
||||
" routing.AddDisjunction([index_0, index_1, index_2, index_3], penalty, 1)\n",
|
||||
"\n",
|
||||
" # Add time window constraints for each vehicle start node.\n",
|
||||
" depot_idx = data['depot']\n",
|
||||
" for vehicle_id in range(data['num_vehicles']):\n",
|
||||
" depot_idx = data[\"depot\"]\n",
|
||||
" for vehicle_id in range(data[\"num_vehicles\"]):\n",
|
||||
" index = routing.Start(vehicle_id)\n",
|
||||
" time_dimension.CumulVar(index).SetRange(480, 1020) # (8am, 5pm)\n",
|
||||
"\n",
|
||||
" # Add time window constraints for each vehicle end node.\n",
|
||||
" depot_idx = data['depot']\n",
|
||||
" for vehicle_id in range(data['num_vehicles']):\n",
|
||||
" depot_idx = data[\"depot\"]\n",
|
||||
" for vehicle_id in range(data[\"num_vehicles\"]):\n",
|
||||
" index = routing.End(vehicle_id)\n",
|
||||
" time_dimension.CumulVar(index).SetRange(480, 1020) # (8am, 5pm)\n",
|
||||
"\n",
|
||||
" # Instantiate route start and end times to produce feasible times.\n",
|
||||
" for i in range(data['num_vehicles']):\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",
|
||||
" time_dimension.CumulVar(routing.Start(i))\n",
|
||||
" )\n",
|
||||
" routing.AddVariableMinimizedByFinalizer(time_dimension.CumulVar(routing.End(i)))\n",
|
||||
"\n",
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.time_limit.FromSeconds(1)\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -220,10 +220,10 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.time_limit.FromSeconds(1)\n",
|
||||
"\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(manager, routing, solution):\n",
|
||||
@@ -149,10 +149,10 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.log_search = True\n",
|
||||
" search_parameters.time_limit.FromSeconds(5)\n",
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
@@ -133,7 +133,6 @@
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(routes, cumul_data):\n",
|
||||
" \"\"\"Print the solution.\"\"\"\n",
|
||||
" total_time = 0\n",
|
||||
@@ -169,7 +168,6 @@
|
||||
" print(route_str)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\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",
|
||||
@@ -185,7 +183,6 @@
|
||||
" return routes\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\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",
|
||||
@@ -207,7 +204,6 @@
|
||||
" return cumul_data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" \"\"\"Solve the VRP with time windows.\"\"\"\n",
|
||||
" # Instantiate the data problem.\n",
|
||||
@@ -268,7 +264,7 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -72,8 +72,8 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Cutting stock problem with the objective to minimize wasted space.\n",
|
||||
"\n"
|
||||
"\n",
|
||||
"Cutting stock problem with the objective to minimize wasted space.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -85,9 +85,10 @@
|
||||
"source": [
|
||||
"import collections\n",
|
||||
"import time\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"from ortools.sat.colab import flags\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"from google.protobuf import text_format\n",
|
||||
"from ortools.linear_solver.python import model_builder as mb\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
@@ -95,13 +96,14 @@
|
||||
"FLAGS = flags.FLAGS\n",
|
||||
"\n",
|
||||
"_OUTPUT_PROTO = flags.define_string(\n",
|
||||
" 'output_proto', '', 'Output file to write the cp_model proto to.')\n",
|
||||
" \"output_proto\", \"\", \"Output file to write the cp_model proto to.\"\n",
|
||||
")\n",
|
||||
"_PARAMS = flags.define_string(\n",
|
||||
" 'params',\n",
|
||||
" 'num_search_workers:8,log_search_progress:true,max_time_in_seconds:10',\n",
|
||||
" 'Sat solver parameters.')\n",
|
||||
"_SOLVER = flags.define_string(\n",
|
||||
" 'solver', 'sat', 'Method used to solve: sat, mip.')\n",
|
||||
" \"params\",\n",
|
||||
" \"num_search_workers:8,log_search_progress:true,max_time_in_seconds:10\",\n",
|
||||
" \"Sat solver parameters.\",\n",
|
||||
")\n",
|
||||
"_SOLVER = flags.define_string(\"solver\", \"sat\", \"Method used to solve: sat, mip.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"DESIRED_LENGTHS = [\n",
|
||||
@@ -173,9 +175,9 @@
|
||||
" states.append(new_state)\n",
|
||||
" state_to_index[new_state] = new_state_index\n",
|
||||
" # Add the transition\n",
|
||||
" transitions.append([\n",
|
||||
" current_state_index, new_state_index, item_index, card + 1\n",
|
||||
" ])\n",
|
||||
" transitions.append(\n",
|
||||
" [current_state_index, new_state_index, item_index, card + 1]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" return states, transitions\n",
|
||||
"\n",
|
||||
@@ -183,14 +185,19 @@
|
||||
"def solve_cutting_stock_with_arc_flow_and_sat(output_proto_file: str, params: str):\n",
|
||||
" \"\"\"Solve the cutting stock with arc-flow and the CP-SAT solver.\"\"\"\n",
|
||||
" items = regroup_and_count(DESIRED_LENGTHS)\n",
|
||||
" print('Items:', items)\n",
|
||||
" print(\"Items:\", items)\n",
|
||||
" num_items = len(DESIRED_LENGTHS)\n",
|
||||
"\n",
|
||||
" max_capacity = max(POSSIBLE_CAPACITIES)\n",
|
||||
" states, transitions = create_state_graph(items, max_capacity)\n",
|
||||
"\n",
|
||||
" print('Dynamic programming has generated', len(states), 'states and',\n",
|
||||
" len(transitions), 'transitions')\n",
|
||||
" print(\n",
|
||||
" \"Dynamic programming has generated\",\n",
|
||||
" len(states),\n",
|
||||
" \"states and\",\n",
|
||||
" len(transitions),\n",
|
||||
" \"transitions\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" incoming_vars = collections.defaultdict(list)\n",
|
||||
" outgoing_vars = collections.defaultdict(list)\n",
|
||||
@@ -208,8 +215,8 @@
|
||||
" count = items[item_index][1]\n",
|
||||
" max_count = count // card\n",
|
||||
" count_var = model.NewIntVar(\n",
|
||||
" 0, max_count,\n",
|
||||
" 'i%i_f%i_t%i_C%s' % (item_index, incoming, outgoing, card))\n",
|
||||
" 0, max_count, \"i%i_f%i_t%i_C%s\" % (item_index, incoming, outgoing, card)\n",
|
||||
" )\n",
|
||||
" incoming_vars[incoming].append(count_var)\n",
|
||||
" outgoing_vars[outgoing].append(count_var)\n",
|
||||
" item_vars[item_index].append(count_var)\n",
|
||||
@@ -219,7 +226,7 @@
|
||||
" for state_index, state in enumerate(states):\n",
|
||||
" if state_index == 0:\n",
|
||||
" continue\n",
|
||||
" exit_var = model.NewIntVar(0, num_items, 'e%i' % state_index)\n",
|
||||
" exit_var = model.NewIntVar(0, num_items, \"e%i\" % state_index)\n",
|
||||
" outgoing_vars[state_index].append(exit_var)\n",
|
||||
" incoming_sink_vars.append(exit_var)\n",
|
||||
" price = price_usage(state, POSSIBLE_CAPACITIES)\n",
|
||||
@@ -228,8 +235,7 @@
|
||||
"\n",
|
||||
" # Flow conservation\n",
|
||||
" for state_index in range(1, len(states)):\n",
|
||||
" model.Add(\n",
|
||||
" sum(incoming_vars[state_index]) == sum(outgoing_vars[state_index]))\n",
|
||||
" model.Add(sum(incoming_vars[state_index]) == sum(outgoing_vars[state_index]))\n",
|
||||
"\n",
|
||||
" # Flow going out of the source must go in the sink\n",
|
||||
" model.Add(sum(outgoing_vars[0]) == sum(incoming_sink_vars))\n",
|
||||
@@ -238,13 +244,17 @@
|
||||
" for item_index, size_and_count in enumerate(items):\n",
|
||||
" num_arcs = len(item_vars[item_index])\n",
|
||||
" model.Add(\n",
|
||||
" sum(item_vars[item_index][i] * item_coeffs[item_index][i]\n",
|
||||
" for i in range(num_arcs)) == size_and_count[1])\n",
|
||||
" sum(\n",
|
||||
" item_vars[item_index][i] * item_coeffs[item_index][i]\n",
|
||||
" for i in range(num_arcs)\n",
|
||||
" )\n",
|
||||
" == size_and_count[1]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Objective is the sum of waste\n",
|
||||
" model.Minimize(\n",
|
||||
" sum(objective_vars[i] * objective_coeffs[i]\n",
|
||||
" for i in range(len(objective_vars))))\n",
|
||||
" sum(objective_vars[i] * objective_coeffs[i] for i in range(len(objective_vars)))\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Output model proto to file.\n",
|
||||
" if output_proto_file:\n",
|
||||
@@ -261,13 +271,18 @@
|
||||
"def solve_cutting_stock_with_arc_flow_and_mip():\n",
|
||||
" \"\"\"Solve the cutting stock with arc-flow and a MIP solver.\"\"\"\n",
|
||||
" items = regroup_and_count(DESIRED_LENGTHS)\n",
|
||||
" print('Items:', items)\n",
|
||||
" print(\"Items:\", items)\n",
|
||||
" num_items = len(DESIRED_LENGTHS)\n",
|
||||
" max_capacity = max(POSSIBLE_CAPACITIES)\n",
|
||||
" states, transitions = create_state_graph(items, max_capacity)\n",
|
||||
"\n",
|
||||
" print('Dynamic programming has generated', len(states), 'states and',\n",
|
||||
" len(transitions), 'transitions')\n",
|
||||
" print(\n",
|
||||
" \"Dynamic programming has generated\",\n",
|
||||
" len(states),\n",
|
||||
" \"states and\",\n",
|
||||
" len(transitions),\n",
|
||||
" \"transitions\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" incoming_vars = collections.defaultdict(list)\n",
|
||||
" outgoing_vars = collections.defaultdict(list)\n",
|
||||
@@ -285,8 +300,10 @@
|
||||
" for outgoing, incoming, item_index, card in transitions:\n",
|
||||
" count = items[item_index][1]\n",
|
||||
" count_var = model.new_int_var(\n",
|
||||
" 0, count, 'a%i_i%i_f%i_t%i_c%i' % (var_index, item_index, incoming,\n",
|
||||
" outgoing, card))\n",
|
||||
" 0,\n",
|
||||
" count,\n",
|
||||
" \"a%i_i%i_f%i_t%i_c%i\" % (var_index, item_index, incoming, outgoing, card),\n",
|
||||
" )\n",
|
||||
" var_index += 1\n",
|
||||
" incoming_vars[incoming].append(count_var)\n",
|
||||
" outgoing_vars[outgoing].append(count_var)\n",
|
||||
@@ -296,7 +313,7 @@
|
||||
" for state_index, state in enumerate(states):\n",
|
||||
" if state_index == 0:\n",
|
||||
" continue\n",
|
||||
" exit_var = model.new_int_var(0, num_items, 'e%i' % state_index)\n",
|
||||
" exit_var = model.new_int_var(0, num_items, \"e%i\" % state_index)\n",
|
||||
" outgoing_vars[state_index].append(exit_var)\n",
|
||||
" incoming_sink_vars.append(exit_var)\n",
|
||||
" price = price_usage(state, POSSIBLE_CAPACITIES)\n",
|
||||
@@ -306,41 +323,49 @@
|
||||
" # Flow conservation\n",
|
||||
" for state_index in range(1, len(states)):\n",
|
||||
" model.add(\n",
|
||||
" mb.LinearExpr.sum(incoming_vars[state_index]) == mb.LinearExpr.sum(\n",
|
||||
" outgoing_vars[state_index]))\n",
|
||||
" mb.LinearExpr.sum(incoming_vars[state_index])\n",
|
||||
" == mb.LinearExpr.sum(outgoing_vars[state_index])\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Flow going out of the source must go in the sink\n",
|
||||
" model.add(\n",
|
||||
" mb.LinearExpr.sum(outgoing_vars[0]) == mb.LinearExpr.sum(\n",
|
||||
" incoming_sink_vars))\n",
|
||||
" mb.LinearExpr.sum(outgoing_vars[0]) == mb.LinearExpr.sum(incoming_sink_vars)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Items must be placed\n",
|
||||
" for item_index, size_and_count in enumerate(items):\n",
|
||||
" num_arcs = len(item_vars[item_index])\n",
|
||||
" model.add(\n",
|
||||
" mb.LinearExpr.sum([item_vars[item_index][i] * item_coeffs[item_index][i]\n",
|
||||
" for i in range(num_arcs)]) == size_and_count[1])\n",
|
||||
" mb.LinearExpr.sum(\n",
|
||||
" [\n",
|
||||
" item_vars[item_index][i] * item_coeffs[item_index][i]\n",
|
||||
" for i in range(num_arcs)\n",
|
||||
" ]\n",
|
||||
" )\n",
|
||||
" == size_and_count[1]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Objective is the sum of waste\n",
|
||||
" model.minimize(np.dot(objective_vars, objective_coeffs))\n",
|
||||
"\n",
|
||||
" solver = mb.ModelSolver('scip')\n",
|
||||
" solver = mb.ModelSolver(\"scip\")\n",
|
||||
" solver.enable_output(True)\n",
|
||||
" status = solver.solve(model)\n",
|
||||
"\n",
|
||||
" ### Output the solution.\n",
|
||||
" if status == mb.SolveStatus.OPTIMAL or status == mb.SolveStatus.FEASIBLE:\n",
|
||||
" print('Objective value = %f found in %.2f s' %\n",
|
||||
" (solver.objective_value, time.time() - start_time))\n",
|
||||
" print(\n",
|
||||
" \"Objective value = %f found in %.2f s\"\n",
|
||||
" % (solver.objective_value, time.time() - start_time)\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('No solution')\n",
|
||||
" print(\"No solution\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(_):\n",
|
||||
" \"\"\"Main function\"\"\"\n",
|
||||
" if _SOLVER.value == 'sat':\n",
|
||||
" solve_cutting_stock_with_arc_flow_and_sat(_OUTPUT_PROTO.value,\n",
|
||||
" _PARAMS.value)\n",
|
||||
" \"\"\"Main function.\"\"\"\n",
|
||||
" if _SOLVER.value == \"sat\":\n",
|
||||
" solve_cutting_stock_with_arc_flow_and_sat(_OUTPUT_PROTO.value, _PARAMS.value)\n",
|
||||
" else: # 'mip'\n",
|
||||
" solve_cutting_stock_with_arc_flow_and_mip()\n",
|
||||
"\n",
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
"from matplotlib import pyplot as plt\n",
|
||||
"from collections import namedtuple\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"from datetime import datetime, timedelta\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -693,7 +693,7 @@
|
||||
" parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" # Setting first solution heuristic (cheapest addition).\n",
|
||||
" parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" # Routing: forbids use of TSPOpt neighborhood, (this is the default behaviour)\n",
|
||||
" parameters.local_search_operators.use_tsp_opt = pywrapcp.BOOL_FALSE\n",
|
||||
" # Disabling Large Neighborhood Search, (this is the default behaviour)\n",
|
||||
|
||||
264
examples/notebook/examples/pentominoes_sat.ipynb
Normal file
264
examples/notebook/examples/pentominoes_sat.ipynb
Normal file
@@ -0,0 +1,264 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "google",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"##### Copyright 2023 Google LLC."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "apache",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
||||
"you may not use this file except in compliance with the License.\n",
|
||||
"You may obtain a copy of the License at\n",
|
||||
"\n",
|
||||
" http://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"\n",
|
||||
"Unless required by applicable law or agreed to in writing, software\n",
|
||||
"distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"See the License for the specific language governing permissions and\n",
|
||||
"limitations under the License.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "basename",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# pentominoes_sat"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "link",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<table align=\"left\">\n",
|
||||
"<td>\n",
|
||||
"<a href=\"https://colab.research.google.com/github/google/or-tools/blob/main/examples/notebook/examples/pentominoes_sat.ipynb\"><img src=\"https://raw.githubusercontent.com/google/or-tools/main/tools/colab_32px.png\"/>Run in Google Colab</a>\n",
|
||||
"</td>\n",
|
||||
"<td>\n",
|
||||
"<a href=\"https://github.com/google/or-tools/blob/main/examples/python/pentominoes_sat.py\"><img src=\"https://raw.githubusercontent.com/google/or-tools/main/tools/github_32px.png\"/>View source on GitHub</a>\n",
|
||||
"</td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "doc",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "install",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install ortools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Example to solves a pentomino paving problem.\n",
|
||||
"\n",
|
||||
"Given a subset of n different pentomino, the problem is to pave a square of\n",
|
||||
"size 5 x n. The problem is reduced to an exact set cover problem and encoded\n",
|
||||
"as a linear boolean problem.\n",
|
||||
"\n",
|
||||
"This problem comes from the game Katamino:\n",
|
||||
"http://boardgamegeek.com/boardgame/6931/katamino\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "code",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from collections.abc import Sequence\n",
|
||||
"from typing import Dict, List\n",
|
||||
"\n",
|
||||
"from ortools.sat.colab import flags\n",
|
||||
"from google.protobuf import text_format\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"_PARAMS = flags.define_string(\n",
|
||||
" \"params\",\n",
|
||||
" \"num_search_workers:16,log_search_progress:false,max_time_in_seconds:45\",\n",
|
||||
" \"Sat solver parameters.\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"_PIECES = flags.define_string(\n",
|
||||
" \"pieces\", \"FILNPTUVWXYZ\", \"The subset of pieces to consider.\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def is_one(mask: List[List[int]], x: int, y: int, orientation: int) -> bool:\n",
|
||||
" \"\"\"Returns true if the oriented piece is 1 at position [i][j].\n",
|
||||
"\n",
|
||||
" The 3 bits in orientation respectively mean: transposition, symmetry by\n",
|
||||
" x axis, symmetry by y axis.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" mask: The shape of the piece.\n",
|
||||
" x: position.\n",
|
||||
" y: position.\n",
|
||||
" orientation: between 0 and 7.\n",
|
||||
" \"\"\"\n",
|
||||
" if orientation & 1:\n",
|
||||
" tmp: int = x\n",
|
||||
" x = y\n",
|
||||
" y = tmp\n",
|
||||
" if orientation & 2:\n",
|
||||
" x = len(mask[0]) - 1 - x\n",
|
||||
" if orientation & 4:\n",
|
||||
" y = len(mask) - 1 - y\n",
|
||||
" return mask[y][x] == 1\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_height(mask: List[List[int]], orientation: int) -> int:\n",
|
||||
" if orientation & 1:\n",
|
||||
" return len(mask[0])\n",
|
||||
" return len(mask)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_width(mask: List[List[int]], orientation: int) -> int:\n",
|
||||
" if orientation & 1:\n",
|
||||
" return len(mask)\n",
|
||||
" return len(mask[0])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def orientation_is_redundant(mask: List[List[int]], orientation: int) -> bool:\n",
|
||||
" \"\"\"Checks if the current rotated figure is the same as a previous rotation.\"\"\"\n",
|
||||
" size_i: int = get_width(mask, orientation)\n",
|
||||
" size_j: int = get_height(mask, orientation)\n",
|
||||
" for o in range(orientation):\n",
|
||||
" if size_i != get_width(mask, o):\n",
|
||||
" continue\n",
|
||||
" if size_j != get_height(mask, o):\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" is_the_same: bool = True\n",
|
||||
" for k in range(size_i):\n",
|
||||
" if not is_the_same:\n",
|
||||
" break\n",
|
||||
" for l in range(size_j):\n",
|
||||
" if not is_the_same:\n",
|
||||
" break\n",
|
||||
" if is_one(mask, k, l, orientation) != is_one(mask, k, l, o):\n",
|
||||
" is_the_same = False\n",
|
||||
" if is_the_same:\n",
|
||||
" return True\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def generate_and_solve_problem(pieces: Dict[str, List[List[int]]]) -> None:\n",
|
||||
" \"\"\"Solves the pentominoes problem.\"\"\"\n",
|
||||
" box_width = len(pieces)\n",
|
||||
" box_height = 5\n",
|
||||
"\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
" position_to_variables: List[List[List[cp_model.IntVar]]] = [\n",
|
||||
" [[] for _ in range(box_width)] for _ in range(box_height)\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" for name, mask in pieces.items():\n",
|
||||
" all_position_variables = []\n",
|
||||
" for orientation in range(8):\n",
|
||||
" if orientation_is_redundant(mask, orientation):\n",
|
||||
" continue\n",
|
||||
" piece_width = get_width(mask, orientation)\n",
|
||||
" piece_height = get_height(mask, orientation)\n",
|
||||
" for i in range(box_width - piece_width + 1):\n",
|
||||
" for j in range(box_height - piece_height + 1):\n",
|
||||
" v = model.new_bool_var(name)\n",
|
||||
" all_position_variables.append(v)\n",
|
||||
" for k in range(piece_width):\n",
|
||||
" for l in range(piece_height):\n",
|
||||
" if is_one(mask, k, l, orientation):\n",
|
||||
" position_to_variables[j + l][i + k].append(v)\n",
|
||||
"\n",
|
||||
" # Only one combination is selected.\n",
|
||||
" model.add_exactly_one(all_position_variables)\n",
|
||||
"\n",
|
||||
" for one_column in position_to_variables:\n",
|
||||
" for all_pieces_in_one_position in one_column:\n",
|
||||
" model.add_exactly_one(all_pieces_in_one_position)\n",
|
||||
"\n",
|
||||
" # Solve the model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" if _PARAMS.value:\n",
|
||||
" text_format.Parse(_PARAMS.value, solver.parameters)\n",
|
||||
" status = solver.solve(model)\n",
|
||||
"\n",
|
||||
" print(\n",
|
||||
" f\"Problem {_PIECES.value} solved in {solver.wall_time}s with status\"\n",
|
||||
" f\" {solver.status_name(status)}\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Print the solution.\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" for y in range(box_height):\n",
|
||||
" line = \"\"\n",
|
||||
" for x in range(box_width):\n",
|
||||
" for v in position_to_variables[y][x]:\n",
|
||||
" if solver.BooleanValue(v):\n",
|
||||
" line += v.name\n",
|
||||
" break\n",
|
||||
" print(line)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
"\n",
|
||||
" # Pieces are stored in a matrix. mask[height][width]\n",
|
||||
" pieces: Dict[str, List[List[int]]] = {\n",
|
||||
" \"F\": [[0, 1, 1], [1, 1, 0], [0, 1, 0]],\n",
|
||||
" \"I\": [[1, 1, 1, 1, 1]],\n",
|
||||
" \"L\": [[1, 1, 1, 1], [1, 0, 0, 0]],\n",
|
||||
" \"N\": [[1, 1, 1, 0], [0, 0, 1, 1]],\n",
|
||||
" \"P\": [[1, 1, 1], [1, 1, 0]],\n",
|
||||
" \"T\": [[1, 1, 1], [0, 1, 0], [0, 1, 0]],\n",
|
||||
" \"U\": [[1, 0, 1], [1, 1, 1]],\n",
|
||||
" \"V\": [[1, 0, 0], [1, 0, 0], [1, 1, 1]],\n",
|
||||
" \"W\": [[1, 0, 0], [1, 1, 0], [0, 1, 1]],\n",
|
||||
" \"X\": [[0, 1, 0], [1, 1, 1], [0, 1, 0]],\n",
|
||||
" \"Y\": [[1, 1, 1, 1], [0, 1, 0, 0]],\n",
|
||||
" \"Z\": [[1, 1, 0], [0, 1, 0], [0, 1, 1]],\n",
|
||||
" }\n",
|
||||
" selected_pieces: Dict[str, List[List[int]]] = {}\n",
|
||||
" for p in _PIECES.value:\n",
|
||||
" if p not in pieces:\n",
|
||||
" print(f\"Piece {p} not found in the list of pieces\")\n",
|
||||
" return\n",
|
||||
" selected_pieces[p] = pieces[p]\n",
|
||||
" generate_and_solve_problem(selected_pieces)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -82,7 +82,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"\n",
|
||||
"DISTANCE_MATRIX = [\n",
|
||||
@@ -216,9 +216,9 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" search_parameters.time_limit.FromSeconds(15)\n",
|
||||
" #search_parameters.log_search = True\n",
|
||||
"\n",
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"\n",
|
||||
"DISTANCE_MATRIX = [\n",
|
||||
@@ -222,9 +222,9 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" search_parameters.time_limit.FromSeconds(15)\n",
|
||||
" #search_parameters.log_search = True\n",
|
||||
"\n",
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
"from functools import partial\n",
|
||||
"import random\n",
|
||||
"\n",
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"\n",
|
||||
"parser = argparse.ArgumentParser()\n",
|
||||
@@ -166,7 +166,7 @@
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" # Setting first solution heuristic (cheapest addition).\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
"\n",
|
||||
" # Setting the cost function.\n",
|
||||
" # Put a callback to the distance accessor here. The callback takes two\n",
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import collections\n",
|
||||
"import time\n",
|
||||
"from typing import Optional\n",
|
||||
"\n",
|
||||
"from ortools.sat.colab import flags\n",
|
||||
@@ -117,9 +118,9 @@
|
||||
" + \" precedence graph.\",\n",
|
||||
")\n",
|
||||
"_DELAY_TIME_LIMIT = flags.define_float(\n",
|
||||
" \"delay_time_limit\",\n",
|
||||
" 20.0,\n",
|
||||
" \"Time limit when computing min delay between tasks.\"\n",
|
||||
" \"pairwise_delay_total_time_limit\",\n",
|
||||
" 120.0,\n",
|
||||
" \"Total time limit when computing min delay between tasks.\"\n",
|
||||
" + \" A non-positive time limit disable min delays computation.\",\n",
|
||||
")\n",
|
||||
"_PREEMPTIVE_LB_TIME_LIMIT = flags.define_float(\n",
|
||||
@@ -668,21 +669,30 @@
|
||||
" ):\n",
|
||||
" return delays, None, False\n",
|
||||
"\n",
|
||||
" time_limit = _DELAY_TIME_LIMIT.value\n",
|
||||
" complete_problem_assignment = None\n",
|
||||
" num_optimal_delays = 0\n",
|
||||
" num_delays_not_found = 0\n",
|
||||
" optimal_found = True\n",
|
||||
" for start_task, end_task, active_tasks in task_intervals:\n",
|
||||
" if time_limit <= 0:\n",
|
||||
" optimal_found = False\n",
|
||||
" print(f\" - #timeout ({_DELAY_TIME_LIMIT.value}s) reached\", flush=True)\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
" start_time = time.time()\n",
|
||||
" min_delay, feasible_delay, assignment = solve_rcpsp(\n",
|
||||
" problem,\n",
|
||||
" \"\",\n",
|
||||
" f\"num_search_workers:16,max_time_in_seconds:{_DELAY_TIME_LIMIT.value}\",\n",
|
||||
" f\"num_search_workers:16,max_time_in_seconds:{time_limit}\",\n",
|
||||
" set(active_tasks),\n",
|
||||
" start_task,\n",
|
||||
" end_task,\n",
|
||||
" [],\n",
|
||||
" delays,\n",
|
||||
" )\n",
|
||||
" time_limit -= time.time() - start_time\n",
|
||||
"\n",
|
||||
" if min_delay != -1:\n",
|
||||
" delays[(start_task, end_task)] = min_delay, feasible_delay\n",
|
||||
" if start_task == 0 and end_task == len(problem.tasks) - 1:\n",
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
")\n",
|
||||
"_PARAMS = flags.define_string(\n",
|
||||
" \"params\",\n",
|
||||
" \"num_search_workers:16,log_search_progress:true,max_time_in_seconds:45\",\n",
|
||||
" \"num_search_workers:16,log_search_progress:false,max_time_in_seconds:45\",\n",
|
||||
" \"Sat solver parameters.\",\n",
|
||||
")\n",
|
||||
"_PREPROCESS = flags.define_bool(\n",
|
||||
@@ -571,6 +571,7 @@
|
||||
" if parameters:\n",
|
||||
" text_format.Parse(parameters, solver.parameters)\n",
|
||||
" solution_printer = SolutionPrinter()\n",
|
||||
" solver.best_bound_callback = lambda a : print(f\"New objective lower bound: {a}\")\n",
|
||||
" solver.solve(model, solution_printer)\n",
|
||||
" for job_id in all_jobs:\n",
|
||||
" print(\n",
|
||||
|
||||
@@ -89,7 +89,6 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.constraint_solver import pywrapcp\n",
|
||||
"from ortools.constraint_solver import routing_enums_pb2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"###########################\n",
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
" for arc in range(smcf.num_arcs()):\n",
|
||||
" # Can ignore arcs leading out of source or into sink.\n",
|
||||
" if smcf.tail(arc) != source and smcf.head(arc) != sink:\n",
|
||||
"\n",
|
||||
" # Arcs in the solution have a flow value of 1. Their start and end nodes\n",
|
||||
" # give an assignment of worker to task.\n",
|
||||
" if smcf.flow(arc) > 0:\n",
|
||||
|
||||
@@ -161,6 +161,7 @@
|
||||
" and smcf.tail(arc) != 12\n",
|
||||
" and smcf.head(arc) != sink\n",
|
||||
" ):\n",
|
||||
"\n",
|
||||
" # Arcs in the solution will have a flow value of 1.\n",
|
||||
" # There start and end nodes give an assignment of worker to task.\n",
|
||||
" if smcf.flow(arc) > 0:\n",
|
||||
|
||||
@@ -83,43 +83,66 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.init.python import init\n",
|
||||
"from ortools.linear_solver import pywraplp\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" print(\"Google OR-Tools version:\", init.OrToolsVersion.version_string())\n",
|
||||
"\n",
|
||||
" # Create the linear solver with the GLOP backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"GLOP\")\n",
|
||||
" if not solver:\n",
|
||||
" print(\"Could not create solver GLOP\")\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" # Create the variables x and y.\n",
|
||||
" x = solver.NumVar(0, 1, \"x\")\n",
|
||||
" y = solver.NumVar(0, 2, \"y\")\n",
|
||||
" x_var = solver.NumVar(0, 1, \"x\")\n",
|
||||
" y_var = solver.NumVar(0, 2, \"y\")\n",
|
||||
"\n",
|
||||
" print(\"Number of variables =\", solver.NumVariables())\n",
|
||||
"\n",
|
||||
" # Create a linear constraint, 0 <= x + y <= 2.\n",
|
||||
" ct = solver.Constraint(0, 2, \"ct\")\n",
|
||||
" ct.SetCoefficient(x, 1)\n",
|
||||
" ct.SetCoefficient(y, 1)\n",
|
||||
" infinity = solver.infinity()\n",
|
||||
" # Create a linear constraint, x + y <= 2.\n",
|
||||
" constraint = solver.Constraint(-infinity, 2, \"ct\")\n",
|
||||
" constraint.SetCoefficient(x_var, 1)\n",
|
||||
" constraint.SetCoefficient(y_var, 1)\n",
|
||||
"\n",
|
||||
" print(\"Number of constraints =\", solver.NumConstraints())\n",
|
||||
"\n",
|
||||
" # Create the objective function, 3 * x + y.\n",
|
||||
" objective = solver.Objective()\n",
|
||||
" objective.SetCoefficient(x, 3)\n",
|
||||
" objective.SetCoefficient(y, 1)\n",
|
||||
" objective.SetCoefficient(x_var, 3)\n",
|
||||
" objective.SetCoefficient(y_var, 1)\n",
|
||||
" objective.SetMaximization()\n",
|
||||
"\n",
|
||||
" print(f\"Solving with {solver.SolverVersion()}\")\n",
|
||||
" solver.Solve()\n",
|
||||
" result_status = solver.Solve()\n",
|
||||
"\n",
|
||||
" print(f\"Status: {result_status}\")\n",
|
||||
" if result_status != pywraplp.Solver.OPTIMAL:\n",
|
||||
" print(\"The problem does not have an optimal solution!\")\n",
|
||||
" if result_status == pywraplp.Solver.FEASIBLE:\n",
|
||||
" print(\"A potentially suboptimal solution was found\")\n",
|
||||
" else:\n",
|
||||
" print(\"The solver could not solve the problem.\")\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" print(\"Solution:\")\n",
|
||||
" print(\"Objective value =\", objective.Value())\n",
|
||||
" print(\"x =\", x.solution_value())\n",
|
||||
" print(\"y =\", y.solution_value())\n",
|
||||
" print(\"x =\", x_var.solution_value())\n",
|
||||
" print(\"y =\", y_var.solution_value())\n",
|
||||
"\n",
|
||||
" print(\"Advanced usage:\")\n",
|
||||
" print(f\"Problem solved in {solver.wall_time():d} milliseconds\")\n",
|
||||
" print(f\"Problem solved in {solver.iterations():d} iterations\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"init.CppBridge.init_logging(\"basic_example.py\")\n",
|
||||
"cpp_flags = init.CppFlags()\n",
|
||||
"cpp_flags.stderrthreshold = True\n",
|
||||
"cpp_flags.log_prefix = False\n",
|
||||
"init.CppBridge.set_flags(cpp_flags)\n",
|
||||
"main()\n",
|
||||
"\n"
|
||||
]
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" # Create the mip solver with the SCIP backend.\n",
|
||||
" # Create the mip solver with the CP-SAT backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"SAT\")\n",
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
|
||||
213
examples/notebook/routing/tsp.ipynb
Normal file
213
examples/notebook/routing/tsp.ipynb
Normal file
@@ -0,0 +1,213 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "google",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"##### Copyright 2023 Google LLC."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "apache",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
||||
"you may not use this file except in compliance with the License.\n",
|
||||
"You may obtain a copy of the License at\n",
|
||||
"\n",
|
||||
" http://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"\n",
|
||||
"Unless required by applicable law or agreed to in writing, software\n",
|
||||
"distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"See the License for the specific language governing permissions and\n",
|
||||
"limitations under the License.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "basename",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# tsp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "link",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<table align=\"left\">\n",
|
||||
"<td>\n",
|
||||
"<a href=\"https://colab.research.google.com/github/google/or-tools/blob/main/examples/notebook/routing/tsp.ipynb\"><img src=\"https://raw.githubusercontent.com/google/or-tools/main/tools/colab_32px.png\"/>Run in Google Colab</a>\n",
|
||||
"</td>\n",
|
||||
"<td>\n",
|
||||
"<a href=\"https://github.com/google/or-tools/blob/main/ortools/routing/samples/tsp.py\"><img src=\"https://raw.githubusercontent.com/google/or-tools/main/tools/github_32px.png\"/>View source on GitHub</a>\n",
|
||||
"</td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "doc",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "install",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install ortools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Simple Travelling Salesman Problem.\n",
|
||||
"\n",
|
||||
"A description of the problem can be found here:\n",
|
||||
"http://en.wikipedia.org/wiki/Travelling_salesperson_problem.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "code",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.routing import enums_pb2\n",
|
||||
"from ortools.routing import parameters_pb2\n",
|
||||
"from ortools.routing.python import routing_model\n",
|
||||
"\n",
|
||||
"FirstSolutionStrategy = enums_pb2.FirstSolutionStrategy\n",
|
||||
"RoutingSearchStatus = enums_pb2.RoutingSearchStatus\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" # Locations in block units\n",
|
||||
" locations = [\n",
|
||||
" # fmt:off\n",
|
||||
" (4, 4), # depot\n",
|
||||
" (2, 0), (8, 0), # locations to visit\n",
|
||||
" (0, 1), (1, 1),\n",
|
||||
" (5, 2), (7, 2),\n",
|
||||
" (3, 3), (6, 3),\n",
|
||||
" (5, 5), (8, 5),\n",
|
||||
" (1, 6), (2, 6),\n",
|
||||
" (3, 7), (6, 7),\n",
|
||||
" (0, 8), (7, 8)\n",
|
||||
" # fmt:on\n",
|
||||
" ]\n",
|
||||
" # Convert locations in meters using a city block dimension of 114m x 80m.\n",
|
||||
" data[\"locations\"] = [(l[0] * 114, l[1] * 80) for l in locations]\n",
|
||||
" data[\"num_vehicles\"] = 1\n",
|
||||
" data[\"depot\"] = 0\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_distance_callback(data, manager):\n",
|
||||
" \"\"\"Creates callback to return distance between points.\"\"\"\n",
|
||||
" distances_ = {}\n",
|
||||
" index_manager_ = manager\n",
|
||||
" # precompute distance between location to have distance callback in O(1)\n",
|
||||
" for from_counter, from_node in enumerate(data[\"locations\"]):\n",
|
||||
" distances_[from_counter] = {}\n",
|
||||
" for to_counter, to_node in enumerate(data[\"locations\"]):\n",
|
||||
" if from_counter == to_counter:\n",
|
||||
" distances_[from_counter][to_counter] = 0\n",
|
||||
" else:\n",
|
||||
" distances_[from_counter][to_counter] = abs(\n",
|
||||
" from_node[0] - to_node[0]\n",
|
||||
" ) + abs(from_node[1] - to_node[1])\n",
|
||||
"\n",
|
||||
" def distance_callback(from_index, to_index):\n",
|
||||
" \"\"\"Returns the manhattan distance between the two nodes.\"\"\"\n",
|
||||
" # Convert from routing variable Index to distance matrix NodeIndex.\n",
|
||||
" from_node = index_manager_.index_to_node(from_index)\n",
|
||||
" to_node = index_manager_.index_to_node(to_index)\n",
|
||||
" return distances_[from_node][to_node]\n",
|
||||
"\n",
|
||||
" return distance_callback\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(manager, routing, solution):\n",
|
||||
" \"\"\"Prints assignment on console.\"\"\"\n",
|
||||
" status = routing.status()\n",
|
||||
" print(f\"Status: {RoutingSearchStatus.Value.Name(status)}\")\n",
|
||||
" if (\n",
|
||||
" status != RoutingSearchStatus.ROUTING_OPTIMAL\n",
|
||||
" and status != RoutingSearchStatus.ROUTING_SUCCESS\n",
|
||||
" ):\n",
|
||||
" print(\"No solution found!\")\n",
|
||||
" return\n",
|
||||
" print(f\"Objective: {solution.objective_value()}\")\n",
|
||||
" index = routing.start(0)\n",
|
||||
" plan_output = \"Route for vehicle 0:\\n\"\n",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.is_end(index):\n",
|
||||
" plan_output += f\" {manager.index_to_node(index)} ->\"\n",
|
||||
" previous_index = index\n",
|
||||
" index = solution.value(routing.next_var(index))\n",
|
||||
" route_distance += routing.get_arc_cost_for_vehicle(previous_index, index, 0)\n",
|
||||
" plan_output += f\" {manager.index_to_node(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" \"\"\"Entry point of the program.\"\"\"\n",
|
||||
" # Instantiate the data problem.\n",
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = routing_model.RoutingIndexManager(\n",
|
||||
" len(data[\"locations\"]), data[\"num_vehicles\"], data[\"depot\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing = routing_model.RoutingModel(manager)\n",
|
||||
"\n",
|
||||
" # Create and register a transit callback.\n",
|
||||
" distance_callback = create_distance_callback(data, manager)\n",
|
||||
" transit_callback_index = routing.register_transit_callback(distance_callback)\n",
|
||||
"\n",
|
||||
" # Define cost of each arc.\n",
|
||||
" routing.set_arc_cost_evaluator_of_all_vehicles(transit_callback_index)\n",
|
||||
"\n",
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters: parameters_pb2.RoutingSearchParameters = (\n",
|
||||
" routing_model.default_routing_search_parameters()\n",
|
||||
" )\n",
|
||||
" search_parameters.first_solution_strategy = FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.solve()\n",
|
||||
" # solution = routing.solve_with_parameters(search_parameters)\n",
|
||||
"\n",
|
||||
" # Print solution on console.\n",
|
||||
" print_solution(manager, routing, solution)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -169,10 +169,13 @@
|
||||
"\n",
|
||||
" for b in active_bins:\n",
|
||||
" print(f\"Bin {b}\")\n",
|
||||
" items_in_bin = x_values.xs(b, level=\"bin\").loc[lambda x: x].index\n",
|
||||
" for item in items_in_bin:\n",
|
||||
" items_in_active_bin = x_values.xs(b, level=\"bin\").loc[lambda x: x].index\n",
|
||||
" for item in items_in_active_bin:\n",
|
||||
" print(f\" Item {item} - weight {items.loc[item].weight}\")\n",
|
||||
" print(f\" Packed items weight: {items.loc[items_in_bin].sum().to_string()}\")\n",
|
||||
" print(\n",
|
||||
" \" Packed items weight:\"\n",
|
||||
" f\" {items.loc[items_in_active_bin].sum().to_string()}\"\n",
|
||||
" )\n",
|
||||
" print()\n",
|
||||
"\n",
|
||||
" print(f\"Total packed weight: {items.weight.sum()}\")\n",
|
||||
|
||||
150
examples/notebook/sat/bool_and_int_var_product_sample_sat.ipynb
Normal file
150
examples/notebook/sat/bool_and_int_var_product_sample_sat.ipynb
Normal file
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "google",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"##### Copyright 2023 Google LLC."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "apache",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
||||
"you may not use this file except in compliance with the License.\n",
|
||||
"You may obtain a copy of the License at\n",
|
||||
"\n",
|
||||
" http://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"\n",
|
||||
"Unless required by applicable law or agreed to in writing, software\n",
|
||||
"distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"See the License for the specific language governing permissions and\n",
|
||||
"limitations under the License.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "basename",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# bool_and_int_var_product_sample_sat"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "link",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<table align=\"left\">\n",
|
||||
"<td>\n",
|
||||
"<a href=\"https://colab.research.google.com/github/google/or-tools/blob/main/examples/notebook/sat/bool_and_int_var_product_sample_sat.ipynb\"><img src=\"https://raw.githubusercontent.com/google/or-tools/main/tools/colab_32px.png\"/>Run in Google Colab</a>\n",
|
||||
"</td>\n",
|
||||
"<td>\n",
|
||||
"<a href=\"https://github.com/google/or-tools/blob/main/ortools/sat/samples/bool_and_int_var_product_sample_sat.py\"><img src=\"https://raw.githubusercontent.com/google/or-tools/main/tools/github_32px.png\"/>View source on GitHub</a>\n",
|
||||
"</td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "doc",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "install",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install ortools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Code sample that encodes the product of a Boolean and an integer variable.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "code",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback):\n",
|
||||
" \"\"\"Print intermediate solutions.\"\"\"\n",
|
||||
"\n",
|
||||
" def __init__(self, variables: list[cp_model.IntVar]):\n",
|
||||
" cp_model.CpSolverSolutionCallback.__init__(self)\n",
|
||||
" self.__variables = variables\n",
|
||||
"\n",
|
||||
" def on_solution_callback(self) -> None:\n",
|
||||
" for v in self.__variables:\n",
|
||||
" print(f\"{v}={self.value(v)}\", end=\" \")\n",
|
||||
" print()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def build_product_var(\n",
|
||||
" model: cp_model.CpModel, b: cp_model.IntVar, x: cp_model.IntVar, name: str\n",
|
||||
") -> cp_model.IntVar:\n",
|
||||
" \"\"\"Builds the product of a Boolean variable and an integer variable.\"\"\"\n",
|
||||
" p = model.new_int_var_from_domain(\n",
|
||||
" cp_model.Domain.from_flat_intervals(x.proto.domain).union_with(\n",
|
||||
" cp_model.Domain(0, 0)\n",
|
||||
" ),\n",
|
||||
" name,\n",
|
||||
" )\n",
|
||||
" model.add(p == x).only_enforce_if(b)\n",
|
||||
" model.add(p == 0).only_enforce_if(~b)\n",
|
||||
" return p\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def bool_and_int_var_product_sample_sat():\n",
|
||||
" \"\"\"Encoding of the product of two Boolean variables.\n",
|
||||
"\n",
|
||||
" p == x * y, which is the same as p <=> x and y\n",
|
||||
" \"\"\"\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
" b = model.new_bool_var(\"b\")\n",
|
||||
" x = model.new_int_var_from_domain(\n",
|
||||
" cp_model.Domain.from_values([1, 2, 3, 5, 6, 7, 9, 10]), \"x\"\n",
|
||||
" )\n",
|
||||
" p = build_product_var(model, b, x, \"p\")\n",
|
||||
"\n",
|
||||
" # Search for x and b values in increasing order.\n",
|
||||
" model.add_decision_strategy(\n",
|
||||
" [b, x], cp_model.CHOOSE_FIRST, cp_model.SELECT_MIN_VALUE\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create a solver and solve.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" solution_printer = VarArraySolutionPrinter([x, b, p])\n",
|
||||
" solver.parameters.enumerate_all_solutions = True\n",
|
||||
" solver.parameters.search_branching = cp_model.FIXED_SEARCH\n",
|
||||
" solver.solve(model, solution_printer)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"bool_and_int_var_product_sample_sat()\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -73,7 +73,7 @@
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solves a simple scheduling problem with a variable work load.\n"
|
||||
"Solves a scheduling problem with a min and max profile for the work load.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -90,22 +90,38 @@
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model() -> tuple[pd.DataFrame, pd.DataFrame]:\n",
|
||||
" \"\"\"Creates the two dataframes that describes the model.\"\"\"\n",
|
||||
"def create_data_model() -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:\n",
|
||||
" \"\"\"Creates the dataframes that describes the model.\"\"\"\n",
|
||||
"\n",
|
||||
" capacity_str: str = \"\"\"\n",
|
||||
" start_hour capacity\n",
|
||||
" max_load_str: str = \"\"\"\n",
|
||||
" start_hour max_load\n",
|
||||
" 0 0\n",
|
||||
" 2 0\n",
|
||||
" 4 1\n",
|
||||
" 6 3\n",
|
||||
" 8 6\n",
|
||||
" 4 3\n",
|
||||
" 6 6\n",
|
||||
" 8 8\n",
|
||||
" 10 12\n",
|
||||
" 12 8\n",
|
||||
" 14 12\n",
|
||||
" 16 10\n",
|
||||
" 18 4\n",
|
||||
" 20 2\n",
|
||||
" 18 6\n",
|
||||
" 20 4\n",
|
||||
" 22 0\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" min_load_str: str = \"\"\"\n",
|
||||
" start_hour min_load\n",
|
||||
" 0 0\n",
|
||||
" 2 0\n",
|
||||
" 4 0\n",
|
||||
" 6 0\n",
|
||||
" 8 3\n",
|
||||
" 10 3\n",
|
||||
" 12 1\n",
|
||||
" 14 3\n",
|
||||
" 16 3\n",
|
||||
" 18 1\n",
|
||||
" 20 1\n",
|
||||
" 22 0\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
@@ -143,29 +159,74 @@
|
||||
" t30 90 4 2\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" capacity_df = pd.read_table(io.StringIO(capacity_str), sep=r\"\\s+\")\n",
|
||||
" max_load_df = pd.read_table(io.StringIO(max_load_str), sep=r\"\\s+\")\n",
|
||||
" min_load_df = pd.read_table(io.StringIO(min_load_str), sep=r\"\\s+\")\n",
|
||||
" tasks_df = pd.read_table(io.StringIO(tasks_str), index_col=0, sep=r\"\\s+\")\n",
|
||||
" return capacity_df, tasks_df\n",
|
||||
" return max_load_df, min_load_df, tasks_df\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main() -> None:\n",
|
||||
"def check_solution(\n",
|
||||
" tasks: list[tuple[int, int, int]],\n",
|
||||
" min_load_df: pd.DataFrame,\n",
|
||||
" max_load_df: pd.DataFrame,\n",
|
||||
" period_length: int,\n",
|
||||
" horizon: int,\n",
|
||||
") -> bool:\n",
|
||||
" \"\"\"Checks the solution validity against the min and max load constraints.\"\"\"\n",
|
||||
" minutes_per_hour = 60\n",
|
||||
" actual_load_profile = [0 for _ in range(horizon)]\n",
|
||||
" min_load_profile = [0 for _ in range(horizon)]\n",
|
||||
" max_load_profile = [0 for _ in range(horizon)]\n",
|
||||
"\n",
|
||||
" # The complexity of the checker is linear in the number of time points, and\n",
|
||||
" # should be improved.\n",
|
||||
" for task in tasks:\n",
|
||||
" for t in range(task[1]):\n",
|
||||
" actual_load_profile[task[0] + t] += task[2]\n",
|
||||
" for row in max_load_df.itertuples():\n",
|
||||
" for t in range(period_length):\n",
|
||||
" max_load_profile[row.start_hour * minutes_per_hour + t] = row.max_load\n",
|
||||
" for row in min_load_df.itertuples():\n",
|
||||
" for t in range(period_length):\n",
|
||||
" min_load_profile[row.start_hour * minutes_per_hour + t] = row.min_load\n",
|
||||
"\n",
|
||||
" for time in range(horizon):\n",
|
||||
" if actual_load_profile[time] > max_load_profile[time]:\n",
|
||||
" print(\n",
|
||||
" f\"actual load {actual_load_profile[time]} at time {time} is greater\"\n",
|
||||
" f\" than max load {max_load_profile[time]}\"\n",
|
||||
" )\n",
|
||||
" return False\n",
|
||||
" if actual_load_profile[time] < min_load_profile[time]:\n",
|
||||
" print(\n",
|
||||
" f\"actual load {actual_load_profile[time]} at time {time} is\"\n",
|
||||
" f\" less than min load {min_load_profile[time]}\"\n",
|
||||
" )\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(_) -> None:\n",
|
||||
" \"\"\"Create the model and solves it.\"\"\"\n",
|
||||
" capacity_df, tasks_df = create_data_model()\n",
|
||||
" max_load_df, min_load_df, tasks_df = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the model.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
" # Get the max capacity from the capacity dataframe.\n",
|
||||
" max_capacity = capacity_df.capacity.max()\n",
|
||||
" print(f\"Max capacity = {max_capacity}\")\n",
|
||||
" max_load = max_load_df.max_load.max()\n",
|
||||
" print(f\"Max capacity = {max_load}\")\n",
|
||||
" print(f\"#tasks = {len(tasks_df)}\")\n",
|
||||
"\n",
|
||||
" minutes_per_period: int = 120\n",
|
||||
" minutes_per_hour: int = 60\n",
|
||||
" horizon: int = 24 * 60\n",
|
||||
"\n",
|
||||
" # Variables\n",
|
||||
" starts = model.new_int_var_series(\n",
|
||||
" name=\"starts\", lower_bounds=0, upper_bounds=horizon, index=tasks_df.index\n",
|
||||
" name=\"starts\",\n",
|
||||
" lower_bounds=0,\n",
|
||||
" upper_bounds=horizon - tasks_df.duration,\n",
|
||||
" index=tasks_df.index,\n",
|
||||
" )\n",
|
||||
" performed = model.new_bool_var_series(name=\"performed\", index=tasks_df.index)\n",
|
||||
"\n",
|
||||
@@ -177,21 +238,72 @@
|
||||
" are_present=performed,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Set up the profile. We use fixed (intervals, demands) to fill in the space\n",
|
||||
" # between the actual load profile and the max capacity.\n",
|
||||
" time_period_intervals = model.new_fixed_size_interval_var_series(\n",
|
||||
" name=\"time_period_intervals\",\n",
|
||||
" index=capacity_df.index,\n",
|
||||
" starts=capacity_df.start_hour * minutes_per_period,\n",
|
||||
" sizes=minutes_per_period,\n",
|
||||
" # Set up the max profile. We use fixed (intervals, demands) to fill in the\n",
|
||||
" # space between the actual max load profile and the max capacity.\n",
|
||||
" time_period_max_intervals = model.new_fixed_size_interval_var_series(\n",
|
||||
" name=\"time_period_max_intervals\",\n",
|
||||
" index=max_load_df.index,\n",
|
||||
" starts=max_load_df.start_hour * minutes_per_hour,\n",
|
||||
" sizes=minutes_per_hour * 2,\n",
|
||||
" )\n",
|
||||
" time_period_heights = max_capacity - capacity_df.capacity\n",
|
||||
" time_period_max_heights = max_load - max_load_df.max_load\n",
|
||||
"\n",
|
||||
" # Cumulative constraint.\n",
|
||||
" # Cumulative constraint for the max profile.\n",
|
||||
" model.add_cumulative(\n",
|
||||
" intervals.to_list() + time_period_intervals.to_list(),\n",
|
||||
" tasks_df.load.to_list() + time_period_heights.to_list(),\n",
|
||||
" max_capacity,\n",
|
||||
" intervals.to_list() + time_period_max_intervals.to_list(),\n",
|
||||
" tasks_df.load.to_list() + time_period_max_heights.to_list(),\n",
|
||||
" max_load,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Set up complemented intervals (from 0 to start, and from start + size to\n",
|
||||
" # horizon).\n",
|
||||
" prefix_intervals = model.new_optional_interval_var_series(\n",
|
||||
" name=\"prefix_intervals\",\n",
|
||||
" index=tasks_df.index,\n",
|
||||
" starts=0,\n",
|
||||
" sizes=starts,\n",
|
||||
" ends=starts,\n",
|
||||
" are_present=performed,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" suffix_intervals = model.new_optional_interval_var_series(\n",
|
||||
" name=\"suffix_intervals\",\n",
|
||||
" index=tasks_df.index,\n",
|
||||
" starts=starts + tasks_df.duration,\n",
|
||||
" sizes=horizon - starts - tasks_df.duration,\n",
|
||||
" ends=horizon,\n",
|
||||
" are_present=performed,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Set up the min profile. We use complemented intervals to maintain the\n",
|
||||
" # complement of the work load, and fixed intervals to enforce the min\n",
|
||||
" # number of active workers per time period.\n",
|
||||
" #\n",
|
||||
" # Note that this works only if the max load cumulative is also added to the\n",
|
||||
" # model.\n",
|
||||
" time_period_min_intervals = model.new_fixed_size_interval_var_series(\n",
|
||||
" name=\"time_period_min_intervals\",\n",
|
||||
" index=min_load_df.index,\n",
|
||||
" starts=min_load_df.start_hour * minutes_per_hour,\n",
|
||||
" sizes=minutes_per_hour * 2,\n",
|
||||
" )\n",
|
||||
" time_period_min_heights = min_load_df.min_load\n",
|
||||
"\n",
|
||||
" # We take into account optional intervals. The actual capacity of the min load\n",
|
||||
" # cumulative is the sum of all the active demands.\n",
|
||||
" sum_of_demands = sum(tasks_df.load)\n",
|
||||
" complement_capacity = model.new_int_var(0, sum_of_demands, \"complement_capacity\")\n",
|
||||
" model.add(complement_capacity == performed.dot(tasks_df.load))\n",
|
||||
"\n",
|
||||
" # Cumulative constraint for the min profile.\n",
|
||||
" model.add_cumulative(\n",
|
||||
" prefix_intervals.to_list()\n",
|
||||
" + suffix_intervals.to_list()\n",
|
||||
" + time_period_min_intervals.to_list(),\n",
|
||||
" tasks_df.load.to_list()\n",
|
||||
" + tasks_df.load.to_list()\n",
|
||||
" + time_period_min_heights.to_list(),\n",
|
||||
" complement_capacity,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Objective: maximize the value of performed intervals.\n",
|
||||
@@ -202,18 +314,32 @@
|
||||
" # Create the solver and solve the model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" solver.parameters.log_search_progress = True\n",
|
||||
" solver.parameters.num_workers = 8\n",
|
||||
" solver.parameters.num_workers = 16\n",
|
||||
" solver.parameters.max_time_in_seconds = 30.0\n",
|
||||
" status = solver.solve(model)\n",
|
||||
"\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" start_values = solver.values(starts)\n",
|
||||
" performed_values = solver.boolean_values(performed)\n",
|
||||
" tasks: list[tuple[int, int, int]] = []\n",
|
||||
" for task in tasks_df.index:\n",
|
||||
" if performed_values[task]:\n",
|
||||
" print(f\"task {task} starts at {start_values[task]}\")\n",
|
||||
" print(\n",
|
||||
" f'task {task} duration={tasks_df[\"duration\"][task]} '\n",
|
||||
" f'load={tasks_df[\"load\"][task]} starts at {start_values[task]}'\n",
|
||||
" )\n",
|
||||
" tasks.append(\n",
|
||||
" (start_values[task], tasks_df.duration[task], tasks_df.load[task])\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print(f\"task {task} is not performed\")\n",
|
||||
" assert check_solution(\n",
|
||||
" tasks=tasks,\n",
|
||||
" min_load_df=min_load_df,\n",
|
||||
" max_load_df=max_load_df,\n",
|
||||
" period_length=2 * minutes_per_hour,\n",
|
||||
" horizon=horizon,\n",
|
||||
" )\n",
|
||||
" elif status == cp_model.INFEASIBLE:\n",
|
||||
" print(\"No solution found\")\n",
|
||||
" else:\n",
|
||||
|
||||
150
examples/notebook/sat/index_first_boolvar_true_sample_sat.ipynb
Normal file
150
examples/notebook/sat/index_first_boolvar_true_sample_sat.ipynb
Normal file
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "google",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"##### Copyright 2023 Google LLC."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "apache",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
||||
"you may not use this file except in compliance with the License.\n",
|
||||
"You may obtain a copy of the License at\n",
|
||||
"\n",
|
||||
" http://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"\n",
|
||||
"Unless required by applicable law or agreed to in writing, software\n",
|
||||
"distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"See the License for the specific language governing permissions and\n",
|
||||
"limitations under the License.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "basename",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# index_first_boolvar_true_sample_sat"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "link",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<table align=\"left\">\n",
|
||||
"<td>\n",
|
||||
"<a href=\"https://colab.research.google.com/github/google/or-tools/blob/main/examples/notebook/sat/index_first_boolvar_true_sample_sat.ipynb\"><img src=\"https://raw.githubusercontent.com/google/or-tools/main/tools/colab_32px.png\"/>Run in Google Colab</a>\n",
|
||||
"</td>\n",
|
||||
"<td>\n",
|
||||
"<a href=\"https://github.com/google/or-tools/blob/main/ortools/sat/samples/index_first_boolvar_true_sample_sat.py\"><img src=\"https://raw.githubusercontent.com/google/or-tools/main/tools/github_32px.png\"/>View source on GitHub</a>\n",
|
||||
"</td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "doc",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"First, you must install [ortools](https://pypi.org/project/ortools/) package in this colab."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "install",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install ortools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Compute the index of the first Boolean variable set to true.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "code",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback):\n",
|
||||
" \"\"\"Print intermediate solutions.\"\"\"\n",
|
||||
"\n",
|
||||
" def __init__(self, index: cp_model.IntVar, boolvars: list[cp_model.IntVar]):\n",
|
||||
" cp_model.CpSolverSolutionCallback.__init__(self)\n",
|
||||
" self.__index = index\n",
|
||||
" self.__boolvars = boolvars\n",
|
||||
"\n",
|
||||
" def on_solution_callback(self) -> None:\n",
|
||||
" line = \"\"\n",
|
||||
" for v in self.__boolvars:\n",
|
||||
" line += f\"{self.value(v)}\"\n",
|
||||
" line += f\" -> {self.value(self.__index)}\"\n",
|
||||
" print(line)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def index_of_first_bool_at_true_sample_sat():\n",
|
||||
" \"\"\"Compute the index of the first Boolean variable set to true.\"\"\"\n",
|
||||
"\n",
|
||||
" # Model.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
" # Variables\n",
|
||||
" num_bool_vars = 5\n",
|
||||
" bool_vars = [model.new_bool_var(f\"{i}\") for i in range(num_bool_vars)]\n",
|
||||
" index = model.new_int_var(0, num_bool_vars, \"index\")\n",
|
||||
"\n",
|
||||
" # Channeling between the index and the Boolean variables.\n",
|
||||
" model.add_min_equality(\n",
|
||||
" index,\n",
|
||||
" [\n",
|
||||
" num_bool_vars - bool_vars[i] * (num_bool_vars - i)\n",
|
||||
" for i in range(num_bool_vars)\n",
|
||||
" ],\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Flip bool_vars in increasing order.\n",
|
||||
" model.add_decision_strategy(\n",
|
||||
" bool_vars, cp_model.CHOOSE_FIRST, cp_model.SELECT_MIN_VALUE\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create a solver and solve with a fixed search.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
"\n",
|
||||
" # Force the solver to follow the decision strategy exactly.\n",
|
||||
" solver.parameters.search_branching = cp_model.FIXED_SEARCH\n",
|
||||
"\n",
|
||||
" # Search and print out all solutions.\n",
|
||||
" solver.parameters.enumerate_all_solutions = True\n",
|
||||
" solution_printer = VarArraySolutionPrinter(index, bool_vars)\n",
|
||||
" solver.solve(model, solution_printer)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"index_of_first_bool_at_true_sample_sat()\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -83,6 +83,8 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import Union\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -136,7 +138,7 @@
|
||||
" else:\n",
|
||||
" max_shifts_per_nurse = min_shifts_per_nurse + 1\n",
|
||||
" for n in all_nurses:\n",
|
||||
" num_shifts_worked = 0\n",
|
||||
" num_shifts_worked: Union[cp_model.LinearExpr, int] = 0\n",
|
||||
" for d in all_days:\n",
|
||||
" for s in all_shifts:\n",
|
||||
" num_shifts_worked += shifts[(n, d, s)]\n",
|
||||
|
||||
Reference in New Issue
Block a user