examples: Bump notebooks
This commit is contained in:
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"A simple knapsack problem."
|
||||
]
|
||||
},
|
||||
@@ -82,43 +83,48 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.algorithms import pywrapknapsack_solver\n",
|
||||
"from ortools.algorithms.python import knapsack_solver\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" # Create the solver.\n",
|
||||
" solver = pywrapknapsack_solver.KnapsackSolver(\n",
|
||||
" pywrapknapsack_solver.KnapsackSolver.\n",
|
||||
" KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER, 'KnapsackExample')\n",
|
||||
" solver = knapsack_solver.KnapsackSolver(\n",
|
||||
" knapsack_solver.SolverType.KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER,\n",
|
||||
" \"KnapsackExample\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" values = [\n",
|
||||
" 360, 83, 59, 130, 431, 67, 230, 52, 93, 125, 670, 892, 600, 38, 48, 147,\n",
|
||||
" 78, 256, 63, 17, 120, 164, 432, 35, 92, 110, 22, 42, 50, 323, 514, 28,\n",
|
||||
" 87, 73, 78, 15, 26, 78, 210, 36, 85, 189, 274, 43, 33, 10, 19, 389, 276,\n",
|
||||
" 312\n",
|
||||
" # fmt:off\n",
|
||||
" 360, 83, 59, 130, 431, 67, 230, 52, 93, 125, 670, 892, 600, 38, 48, 147,\n",
|
||||
" 78, 256, 63, 17, 120, 164, 432, 35, 92, 110, 22, 42, 50, 323, 514, 28,\n",
|
||||
" 87, 73, 78, 15, 26, 78, 210, 36, 85, 189, 274, 43, 33, 10, 19, 389, 276,\n",
|
||||
" 312\n",
|
||||
" # fmt:on\n",
|
||||
" ]\n",
|
||||
" weights = [\n",
|
||||
" # fmt: off\n",
|
||||
" [7, 0, 30, 22, 80, 94, 11, 81, 70, 64, 59, 18, 0, 36, 3, 8, 15, 42, 9, 0,\n",
|
||||
" 42, 47, 52, 32, 26, 48, 55, 6, 29, 84, 2, 4, 18, 56, 7, 29, 93, 44, 71, 3,\n",
|
||||
" 86, 66, 31, 65, 0, 79, 20, 65, 52, 13],\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" weights = [[\n",
|
||||
" 7, 0, 30, 22, 80, 94, 11, 81, 70, 64, 59, 18, 0, 36, 3, 8, 15, 42, 9, 0,\n",
|
||||
" 42, 47, 52, 32, 26, 48, 55, 6, 29, 84, 2, 4, 18, 56, 7, 29, 93, 44, 71,\n",
|
||||
" 3, 86, 66, 31, 65, 0, 79, 20, 65, 52, 13\n",
|
||||
" ]]\n",
|
||||
" capacities = [850]\n",
|
||||
"\n",
|
||||
" solver.Init(values, weights, capacities)\n",
|
||||
" computed_value = solver.Solve()\n",
|
||||
" solver.init(values, weights, capacities)\n",
|
||||
" computed_value = solver.solve()\n",
|
||||
"\n",
|
||||
" packed_items = []\n",
|
||||
" packed_weights = []\n",
|
||||
" total_weight = 0\n",
|
||||
" print('Total value =', computed_value)\n",
|
||||
" print(\"Total value =\", computed_value)\n",
|
||||
" for i in range(len(values)):\n",
|
||||
" if solver.BestSolutionContains(i):\n",
|
||||
" if solver.best_solution_contains(i):\n",
|
||||
" packed_items.append(i)\n",
|
||||
" packed_weights.append(weights[0][i])\n",
|
||||
" total_weight += weights[0][i]\n",
|
||||
" print('Total weight:', total_weight)\n",
|
||||
" print('Packed items:', packed_items)\n",
|
||||
" print('Packed_weights:', packed_weights)\n",
|
||||
" print(\"Total weight:\", total_weight)\n",
|
||||
" print(\"Packed items:\", packed_items)\n",
|
||||
" print(\"Packed_weights:\", packed_weights)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -83,27 +83,29 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.algorithms import pywrapknapsack_solver\n",
|
||||
"from ortools.algorithms.python import knapsack_solver\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" # Create the solver.\n",
|
||||
" solver = pywrapknapsack_solver.KnapsackSolver(\n",
|
||||
" pywrapknapsack_solver.KnapsackSolver.\n",
|
||||
" KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER, \"test\")\n",
|
||||
" solver = knapsack_solver.KnapsackSolver(\n",
|
||||
" knapsack_solver.SolverType.KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER,\n",
|
||||
" \"test\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" weights = [[\n",
|
||||
" 565, 406, 194, 130, 435, 367, 230, 315, 393, 125, 670, 892, 600, 293,\n",
|
||||
" 712, 147, 421, 255\n",
|
||||
" ]]\n",
|
||||
" weights = [\n",
|
||||
" # fmt:off\n",
|
||||
" [565, 406, 194, 130, 435, 367, 230, 315, 393, 125, 670, 892, 600, 293, 712, 147, 421, 255],\n",
|
||||
" # fmt:on\n",
|
||||
" ]\n",
|
||||
" capacities = [850]\n",
|
||||
" values = weights[0]\n",
|
||||
"\n",
|
||||
" solver.Init(values, weights, capacities)\n",
|
||||
" computed_value = solver.Solve()\n",
|
||||
" solver.init(values, weights, capacities)\n",
|
||||
" computed_value = solver.solve()\n",
|
||||
"\n",
|
||||
" packed_items = [\n",
|
||||
" x for x in range(0, len(weights[0])) if solver.BestSolutionContains(x)\n",
|
||||
" x for x in range(0, len(weights[0])) if solver.best_solution_contains(x)\n",
|
||||
" ]\n",
|
||||
" packed_weights = [weights[0][i] for i in packed_items]\n",
|
||||
"\n",
|
||||
|
||||
@@ -93,23 +93,23 @@
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" # Constraint programming engine\n",
|
||||
" solver = pywrapcp.Solver('CP is fun!')\n",
|
||||
" solver = pywrapcp.Solver(\"CP is fun!\")\n",
|
||||
"\n",
|
||||
" base = 10\n",
|
||||
"\n",
|
||||
" # Decision variables.\n",
|
||||
" digits = list(range(0, base))\n",
|
||||
" digits_without_zero = list(range(1, base))\n",
|
||||
" c = solver.IntVar(digits_without_zero, 'C')\n",
|
||||
" p = solver.IntVar(digits, 'P')\n",
|
||||
" i = solver.IntVar(digits_without_zero, 'I')\n",
|
||||
" s = solver.IntVar(digits, 'S')\n",
|
||||
" f = solver.IntVar(digits_without_zero, 'F')\n",
|
||||
" u = solver.IntVar(digits, 'U')\n",
|
||||
" n = solver.IntVar(digits, 'N')\n",
|
||||
" t = solver.IntVar(digits_without_zero, 'T')\n",
|
||||
" r = solver.IntVar(digits, 'R')\n",
|
||||
" e = solver.IntVar(digits, 'E')\n",
|
||||
" c = solver.IntVar(digits_without_zero, \"C\")\n",
|
||||
" p = solver.IntVar(digits, \"P\")\n",
|
||||
" i = solver.IntVar(digits_without_zero, \"I\")\n",
|
||||
" s = solver.IntVar(digits, \"S\")\n",
|
||||
" f = solver.IntVar(digits_without_zero, \"F\")\n",
|
||||
" u = solver.IntVar(digits, \"U\")\n",
|
||||
" n = solver.IntVar(digits, \"N\")\n",
|
||||
" t = solver.IntVar(digits_without_zero, \"T\")\n",
|
||||
" r = solver.IntVar(digits, \"R\")\n",
|
||||
" e = solver.IntVar(digits, \"E\")\n",
|
||||
"\n",
|
||||
" # We need to group variables in a list to use the constraint AllDifferent.\n",
|
||||
" letters = [c, p, i, s, f, u, n, t, r, e]\n",
|
||||
@@ -121,8 +121,10 @@
|
||||
" solver.Add(solver.AllDifferent(letters))\n",
|
||||
"\n",
|
||||
" # CP + IS + FUN = TRUE\n",
|
||||
" solver.Add(p + s + n + base * (c + i + u) + base * base * f == e +\n",
|
||||
" base * u + base * base * r + base * base * base * t)\n",
|
||||
" solver.Add(\n",
|
||||
" p + s + n + base * (c + i + u) + base * base * f\n",
|
||||
" == e + base * u + base * base * r + base * base * base * t\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" solution_count = 0\n",
|
||||
" db = solver.Phase(letters, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT)\n",
|
||||
@@ -130,13 +132,22 @@
|
||||
" while solver.NextSolution():\n",
|
||||
" print(letters)\n",
|
||||
" # Is CP + IS + FUN = TRUE?\n",
|
||||
" assert (base * c.Value() + p.Value() + base * i.Value() + s.Value() +\n",
|
||||
" base * base * f.Value() + base * u.Value() +\n",
|
||||
" n.Value() == base * base * base * t.Value() +\n",
|
||||
" base * base * r.Value() + base * u.Value() + e.Value())\n",
|
||||
" assert (\n",
|
||||
" base * c.Value()\n",
|
||||
" + p.Value()\n",
|
||||
" + base * i.Value()\n",
|
||||
" + s.Value()\n",
|
||||
" + base * base * f.Value()\n",
|
||||
" + base * u.Value()\n",
|
||||
" + n.Value()\n",
|
||||
" == base * base * base * t.Value()\n",
|
||||
" + base * base * r.Value()\n",
|
||||
" + base * u.Value()\n",
|
||||
" + e.Value()\n",
|
||||
" )\n",
|
||||
" solution_count += 1\n",
|
||||
" solver.EndSearch()\n",
|
||||
" print(f'Number of solutions found: {solution_count}')\n",
|
||||
" print(f\"Number of solutions found: {solution_count}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -100,81 +100,91 @@
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" # Locations in block unit\n",
|
||||
" locations_ = \\\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",
|
||||
" 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",
|
||||
" # 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['numlocations_'] = len(data['locations'])\n",
|
||||
" data['time_windows'] = \\\n",
|
||||
" [(0, 0),\n",
|
||||
" (75, 85), (75, 85), # 1, 2\n",
|
||||
" (60, 70), (45, 55), # 3, 4\n",
|
||||
" (0, 8), (50, 60), # 5, 6\n",
|
||||
" (0, 10), (10, 20), # 7, 8\n",
|
||||
" (0, 10), (75, 85), # 9, 10\n",
|
||||
" (85, 95), (5, 15), # 11, 12\n",
|
||||
" (15, 25), (10, 20), # 13, 14\n",
|
||||
" (45, 55), (30, 40)] # 15, 16\n",
|
||||
" data['demands'] = \\\n",
|
||||
" [0, # depot\n",
|
||||
" 1, 1, # 1, 2\n",
|
||||
" 2, 4, # 3, 4\n",
|
||||
" 2, 4, # 5, 6\n",
|
||||
" 8, 8, # 7, 8\n",
|
||||
" 1, 2, # 9,10\n",
|
||||
" 1, 2, # 11,12\n",
|
||||
" 4, 4, # 13, 14\n",
|
||||
" 8, 8] # 15, 16\n",
|
||||
" data['time_per_demand_unit'] = 5 # 5 minutes/unit\n",
|
||||
" data['num_vehicles'] = 4\n",
|
||||
" data['breaks'] = [(2, False), (2, False), (2, False), (2, False)]\n",
|
||||
" data['vehicle_capacity'] = 15\n",
|
||||
" data['vehicle_speed'] = 83 # Travel speed: 5km/h converted in m/min\n",
|
||||
" data['depot'] = 0\n",
|
||||
" data[\"locations\"] = [(l[0] * 114, l[1] * 80) for l in locations_]\n",
|
||||
" data[\"numlocations_\"] = len(data[\"locations\"])\n",
|
||||
" data[\"time_windows\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" (0, 0), # depot\n",
|
||||
" (75, 85), (75, 85), # 1, 2\n",
|
||||
" (60, 70), (45, 55), # 3, 4\n",
|
||||
" (0, 8), (50, 60), # 5, 6\n",
|
||||
" (0, 10), (10, 20), # 7, 8\n",
|
||||
" (0, 10), (75, 85), # 9, 10\n",
|
||||
" (85, 95), (5, 15), # 11, 12\n",
|
||||
" (15, 25), (10, 20), # 13, 14\n",
|
||||
" (45, 55), (30, 40),\n",
|
||||
" # 15, 16\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" data[\"demands\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" 0, # depot\n",
|
||||
" 1, 1, # 1, 2\n",
|
||||
" 2, 4, # 3, 4\n",
|
||||
" 2, 4, # 5, 6\n",
|
||||
" 8, 8, # 7, 8\n",
|
||||
" 1, 2, # 9, 10\n",
|
||||
" 1, 2, # 11, 12\n",
|
||||
" 4, 4, # 13, 14\n",
|
||||
" 8, 8,\n",
|
||||
" # 15, 16\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" data[\"time_per_demand_unit\"] = 5 # 5 minutes/unit\n",
|
||||
" data[\"num_vehicles\"] = 4\n",
|
||||
" data[\"breaks\"] = [(2, False), (2, False), (2, False), (2, False)]\n",
|
||||
" data[\"vehicle_capacity\"] = 15\n",
|
||||
" data[\"vehicle_speed\"] = 83 # Travel speed: 5km/h converted in m/min\n",
|
||||
" data[\"depot\"] = 0\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\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['numlocations_']):\n",
|
||||
" for from_node in range(data[\"numlocations_\"]):\n",
|
||||
" distances_[from_node] = {}\n",
|
||||
" for to_node in range(data['numlocations_']):\n",
|
||||
" for to_node in range(data[\"numlocations_\"]):\n",
|
||||
" if from_node == to_node:\n",
|
||||
" distances_[from_node][to_node] = 0\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",
|
||||
"\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, node):\n",
|
||||
" \"\"\"Returns the demand of the current node.\"\"\"\n",
|
||||
@@ -185,13 +195,14 @@
|
||||
"\n",
|
||||
"def add_capacity_constraints(routing, data, demand_evaluator_index):\n",
|
||||
" \"\"\"Adds capacity constraint.\"\"\"\n",
|
||||
" capacity = 'Capacity'\n",
|
||||
" capacity = \"Capacity\"\n",
|
||||
" routing.AddDimension(\n",
|
||||
" demand_evaluator_index,\n",
|
||||
" 0, # null capacity slack\n",
|
||||
" data['vehicle_capacity'],\n",
|
||||
" data[\"vehicle_capacity\"],\n",
|
||||
" True, # start cumul to zero\n",
|
||||
" capacity)\n",
|
||||
" capacity,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_time_evaluator(data):\n",
|
||||
@@ -199,63 +210,68 @@
|
||||
"\n",
|
||||
" def service_time(data, node):\n",
|
||||
" \"\"\"Gets the service time for the specified location.\"\"\"\n",
|
||||
" return data['demands'][node] * data['time_per_demand_unit']\n",
|
||||
" return 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['numlocations_']):\n",
|
||||
" for from_node in range(data[\"numlocations_\"]):\n",
|
||||
" total_time_[from_node] = {}\n",
|
||||
" for to_node in range(data['numlocations_']):\n",
|
||||
" for to_node in range(data[\"numlocations_\"]):\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_index):\n",
|
||||
" \"\"\"Add Global Span constraint.\"\"\"\n",
|
||||
" time = 'Time'\n",
|
||||
" time = \"Time\"\n",
|
||||
" horizon = 120\n",
|
||||
" routing.AddDimension(\n",
|
||||
" time_evaluator_index,\n",
|
||||
" horizon, # allow waiting time\n",
|
||||
" horizon, # 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",
|
||||
" # 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",
|
||||
" if location_idx == data['depot']:\n",
|
||||
" for location_idx, time_window in enumerate(data[\"time_windows\"]):\n",
|
||||
" if location_idx == data[\"depot\"]:\n",
|
||||
" continue\n",
|
||||
" index = manager.NodeToIndex(location_idx)\n",
|
||||
" time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])\n",
|
||||
" 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",
|
||||
" # The time window at the end node was impliclty set in the time dimension\n",
|
||||
" # definition to be [0, horizon].\n",
|
||||
@@ -263,28 +279,32 @@
|
||||
" # be added to the assignment.\n",
|
||||
"\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",
|
||||
"\n",
|
||||
" print('Breaks:')\n",
|
||||
" print(\"Breaks:\")\n",
|
||||
" intervals = assignment.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",
|
||||
" total_distance = 0\n",
|
||||
" total_load = 0\n",
|
||||
" total_time = 0\n",
|
||||
" capacity_dimension = routing.GetDimensionOrDie('Capacity')\n",
|
||||
" time_dimension = routing.GetDimensionOrDie('Time')\n",
|
||||
" for vehicle_id in range(data['num_vehicles']):\n",
|
||||
" capacity_dimension = routing.GetDimensionOrDie(\"Capacity\")\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(\"Time\")\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",
|
||||
@@ -292,32 +312,33 @@
|
||||
" slack_var = time_dimension.SlackVar(index)\n",
|
||||
" node = manager.IndexToNode(index)\n",
|
||||
" plan_output += (\n",
|
||||
" f' {node}'\n",
|
||||
" f' Load({assignment.Value(load_var)})'\n",
|
||||
" f' Time({assignment.Min(time_var)}, {assignment.Max(time_var)})'\n",
|
||||
" f' Slack({assignment.Min(slack_var)}, {assignment.Max(slack_var)})'\n",
|
||||
" ' ->')\n",
|
||||
" f\" {node}\"\n",
|
||||
" f\" Load({assignment.Value(load_var)})\"\n",
|
||||
" f\" Time({assignment.Min(time_var)}, {assignment.Max(time_var)})\"\n",
|
||||
" f\" Slack({assignment.Min(slack_var)}, {assignment.Max(slack_var)})\"\n",
|
||||
" \" ->\"\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",
|
||||
" node = manager.IndexToNode(index)\n",
|
||||
" plan_output += (\n",
|
||||
" f' {node}'\n",
|
||||
" f' Load({assignment.Value(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.Value(load_var)}\\n'\n",
|
||||
" plan_output += f'Time of the route: {assignment.Value(time_var)}\\n'\n",
|
||||
" f\" {node}\"\n",
|
||||
" f\" Load({assignment.Value(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.Value(load_var)}\\n\"\n",
|
||||
" plan_output += f\"Time of the route: {assignment.Value(time_var)}\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += distance\n",
|
||||
" total_load += assignment.Value(load_var)\n",
|
||||
" total_time += assignment.Value(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",
|
||||
"def main():\n",
|
||||
@@ -326,50 +347,57 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(data['numlocations_'],\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" data[\"numlocations_\"], 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",
|
||||
" functools.partial(create_distance_evaluator(data), manager))\n",
|
||||
" functools.partial(create_distance_evaluator(data), manager)\n",
|
||||
" )\n",
|
||||
" routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)\n",
|
||||
"\n",
|
||||
" # Add Capacity constraint\n",
|
||||
" demand_evaluator_index = routing.RegisterUnaryTransitCallback(\n",
|
||||
" functools.partial(create_demand_evaluator(data), manager))\n",
|
||||
" functools.partial(create_demand_evaluator(data), manager)\n",
|
||||
" )\n",
|
||||
" add_capacity_constraints(routing, data, demand_evaluator_index)\n",
|
||||
"\n",
|
||||
" # Add Time Window constraint\n",
|
||||
" time_evaluator_index = routing.RegisterTransitCallback(\n",
|
||||
" functools.partial(create_time_evaluator(data), manager))\n",
|
||||
" functools.partial(create_time_evaluator(data), manager)\n",
|
||||
" )\n",
|
||||
" add_time_window_constraints(routing, manager, data, time_evaluator_index)\n",
|
||||
"\n",
|
||||
" # Add breaks\n",
|
||||
" time_dimension = routing.GetDimensionOrDie('Time')\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(\"Time\")\n",
|
||||
" node_visit_transit = {}\n",
|
||||
" for index in range(routing.Size()):\n",
|
||||
" node = manager.IndexToNode(index)\n",
|
||||
" node_visit_transit[index] = int(data['demands'][node] *\n",
|
||||
" data['time_per_demand_unit'])\n",
|
||||
" node_visit_transit[index] = int(\n",
|
||||
" data[\"demands\"][node] * data[\"time_per_demand_unit\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" break_intervals = {}\n",
|
||||
" for v in range(data['num_vehicles']):\n",
|
||||
" vehicle_break = data['breaks'][v]\n",
|
||||
" for v in range(data[\"num_vehicles\"]):\n",
|
||||
" vehicle_break = data[\"breaks\"][v]\n",
|
||||
" break_intervals[v] = [\n",
|
||||
" routing.solver().FixedDurationIntervalVar(15, 100, vehicle_break[0],\n",
|
||||
" vehicle_break[1],\n",
|
||||
" f'Break for vehicle {v}')\n",
|
||||
" routing.solver().FixedDurationIntervalVar(\n",
|
||||
" 15, 100, vehicle_break[0], vehicle_break[1], f\"Break for vehicle {v}\"\n",
|
||||
" )\n",
|
||||
" ]\n",
|
||||
" time_dimension.SetBreakIntervalsOfVehicle(break_intervals[v], v,\n",
|
||||
" node_visit_transit.values())\n",
|
||||
" time_dimension.SetBreakIntervalsOfVehicle(\n",
|
||||
" break_intervals[v], v, node_visit_transit.values()\n",
|
||||
" )\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",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" ) # pylint: disable=no-member\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" assignment = routing.SolveWithParameters(search_parameters)\n",
|
||||
@@ -378,7 +406,7 @@
|
||||
" if assignment:\n",
|
||||
" print_solution(data, manager, routing, assignment)\n",
|
||||
" else:\n",
|
||||
" print('No solution found!')\n",
|
||||
" print(\"No solution found!\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -89,13 +89,11 @@
|
||||
"\n",
|
||||
"def main(board_size):\n",
|
||||
" # Creates the solver.\n",
|
||||
" solver = pywrapcp.Solver('n-queens')\n",
|
||||
" solver = pywrapcp.Solver(\"n-queens\")\n",
|
||||
"\n",
|
||||
" # Creates the variables.\n",
|
||||
" # The array index is the column, and the value is the row.\n",
|
||||
" queens = [\n",
|
||||
" solver.IntVar(0, board_size - 1, f'x{i}') for i in range(board_size)\n",
|
||||
" ]\n",
|
||||
" queens = [solver.IntVar(0, board_size - 1, f\"x{i}\") for i in range(board_size)]\n",
|
||||
"\n",
|
||||
" # Creates the constraints.\n",
|
||||
" # All rows must be different.\n",
|
||||
@@ -105,8 +103,7 @@
|
||||
" solver.Add(solver.AllDifferent([queens[i] + i for i in range(board_size)]))\n",
|
||||
" solver.Add(solver.AllDifferent([queens[i] - i for i in range(board_size)]))\n",
|
||||
"\n",
|
||||
" db = solver.Phase(queens, solver.CHOOSE_FIRST_UNBOUND,\n",
|
||||
" solver.ASSIGN_MIN_VALUE)\n",
|
||||
" db = solver.Phase(queens, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE)\n",
|
||||
"\n",
|
||||
" # Iterates through the solutions, displaying each.\n",
|
||||
" num_solutions = 0\n",
|
||||
@@ -117,20 +114,20 @@
|
||||
" for j in range(board_size):\n",
|
||||
" if queens[j].Value() == i:\n",
|
||||
" # There is a queen in column j, row i.\n",
|
||||
" print('Q', end=' ')\n",
|
||||
" print(\"Q\", end=\" \")\n",
|
||||
" else:\n",
|
||||
" print('_', end=' ')\n",
|
||||
" print(\"_\", end=\" \")\n",
|
||||
" print()\n",
|
||||
" print()\n",
|
||||
" num_solutions += 1\n",
|
||||
" solver.EndSearch()\n",
|
||||
"\n",
|
||||
" # Statistics.\n",
|
||||
" print('\\nStatistics')\n",
|
||||
" print(f' failures: {solver.Failures()}')\n",
|
||||
" print(f' branches: {solver.Branches()}')\n",
|
||||
" print(f' wall time: {solver.WallTime()} ms')\n",
|
||||
" print(f' Solutions found: {num_solutions}')\n",
|
||||
" print(\"\\nStatistics\")\n",
|
||||
" print(f\" failures: {solver.Failures()}\")\n",
|
||||
" print(f\" branches: {solver.Branches()}\")\n",
|
||||
" print(f\" wall time: {solver.WallTime()} ms\")\n",
|
||||
" print(f\" Solutions found: {num_solutions}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# By default, solve the 8x8 problem.\n",
|
||||
|
||||
@@ -89,37 +89,38 @@
|
||||
"def main():\n",
|
||||
" \"\"\"Entry point of the program.\"\"\"\n",
|
||||
" # Instantiate the solver.\n",
|
||||
" solver = pywrapcp.Solver('CPSimple')\n",
|
||||
" solver = pywrapcp.Solver(\"CPSimple\")\n",
|
||||
"\n",
|
||||
" # Create the variables.\n",
|
||||
" num_vals = 3\n",
|
||||
" x = solver.IntVar(0, num_vals - 1, 'x')\n",
|
||||
" y = solver.IntVar(0, num_vals - 1, 'y')\n",
|
||||
" z = solver.IntVar(0, num_vals - 1, 'z')\n",
|
||||
" x = solver.IntVar(0, num_vals - 1, \"x\")\n",
|
||||
" y = solver.IntVar(0, num_vals - 1, \"y\")\n",
|
||||
" z = solver.IntVar(0, num_vals - 1, \"z\")\n",
|
||||
"\n",
|
||||
" # Constraint 0: x != y.\n",
|
||||
" solver.Add(x != y)\n",
|
||||
" print('Number of constraints: ', solver.Constraints())\n",
|
||||
" print(\"Number of constraints: \", solver.Constraints())\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" decision_builder = solver.Phase([x, y, z], solver.CHOOSE_FIRST_UNBOUND,\n",
|
||||
" solver.ASSIGN_MIN_VALUE)\n",
|
||||
" decision_builder = solver.Phase(\n",
|
||||
" [x, y, z], solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Print solution on console.\n",
|
||||
" count = 0\n",
|
||||
" solver.NewSearch(decision_builder)\n",
|
||||
" while solver.NextSolution():\n",
|
||||
" count += 1\n",
|
||||
" solution = f'Solution {count}:\\n'\n",
|
||||
" solution = f\"Solution {count}:\\n\"\n",
|
||||
" for var in [x, y, z]:\n",
|
||||
" solution += f' {var.Name()} = {var.Value()}'\n",
|
||||
" solution += f\" {var.Name()} = {var.Value()}\"\n",
|
||||
" print(solution)\n",
|
||||
" solver.EndSearch()\n",
|
||||
" print(f'Number of solutions found: {count}')\n",
|
||||
" print(f\"Number of solutions found: {count}\")\n",
|
||||
"\n",
|
||||
" print('Advanced usage:')\n",
|
||||
" print(f'Problem solved in {solver.WallTime()}ms')\n",
|
||||
" print(f'Memory usage: {pywrapcp.Solver.MemoryUsage()}bytes')\n",
|
||||
" print(\"Advanced usage:\")\n",
|
||||
" print(f\"Problem solved in {solver.WallTime()}ms\")\n",
|
||||
" print(f\"Memory usage: {pywrapcp.Solver.MemoryUsage()}bytes\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -100,7 +100,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 absolute difference between the two nodes.\"\"\"\n",
|
||||
@@ -117,23 +116,24 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # pylint: disable=no-member\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" ) # pylint: disable=no-member\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" assignment = routing.SolveWithParameters(search_parameters)\n",
|
||||
"\n",
|
||||
" # Print solution on console.\n",
|
||||
" print(f'Objective: {assignment.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {assignment.ObjectiveValue()}\")\n",
|
||||
" index = routing.Start(0)\n",
|
||||
" plan_output = 'Route for vehicle 0:\\n'\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",
|
||||
" plan_output += f\"{manager.IndexToNode(index)} -> \"\n",
|
||||
" previous_index = index\n",
|
||||
" index = assignment.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",
|
||||
" plan_output += f\"{manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -95,20 +95,23 @@
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" # Locations in block units\n",
|
||||
" locations = \\\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",
|
||||
" 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",
|
||||
" 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",
|
||||
@@ -117,15 +120,15 @@
|
||||
" 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",
|
||||
" for from_counter, from_node in enumerate(data[\"locations\"]):\n",
|
||||
" distances_[from_counter] = {}\n",
|
||||
" for to_counter, to_node in enumerate(data['locations']):\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] = (\n",
|
||||
" abs(from_node[0] - to_node[0]) +\n",
|
||||
" abs(from_node[1] - to_node[1]))\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",
|
||||
@@ -139,17 +142,17 @@
|
||||
"\n",
|
||||
"def print_solution(manager, routing, assignment):\n",
|
||||
" \"\"\"Prints assignment on console.\"\"\"\n",
|
||||
" print(f'Objective: {assignment.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {assignment.ObjectiveValue()}\")\n",
|
||||
" index = routing.Start(0)\n",
|
||||
" plan_output = 'Route for vehicle 0:\\n'\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",
|
||||
" plan_output += f\" {manager.IndexToNode(index)} ->\"\n",
|
||||
" previous_index = index\n",
|
||||
" index = assignment.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",
|
||||
" plan_output += f\" {manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -159,8 +162,9 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['locations']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"locations\"]), data[\"num_vehicles\"], data[\"depot\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
@@ -175,7 +179,8 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" assignment = routing.SolveWithParameters(search_parameters)\n",
|
||||
|
||||
@@ -92,57 +92,59 @@
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" # Locations in block units\n",
|
||||
" data['locations'] = [\n",
|
||||
" (288, 149), (288, 129), (270, 133), (256, 141), (256, 157), (246, 157),\n",
|
||||
" (236, 169), (228, 169), (228, 161), (220, 169), (212, 169), (204, 169),\n",
|
||||
" (196, 169), (188, 169), (196, 161), (188, 145), (172, 145), (164, 145),\n",
|
||||
" (156, 145), (148, 145), (140, 145), (148, 169), (164, 169), (172, 169),\n",
|
||||
" (156, 169), (140, 169), (132, 169), (124, 169), (116, 161), (104, 153),\n",
|
||||
" (104, 161), (104, 169), (90, 165), (80, 157), (64, 157), (64, 165),\n",
|
||||
" (56, 169), (56, 161), (56, 153), (56, 145), (56, 137), (56, 129),\n",
|
||||
" (56, 121), (40, 121), (40, 129), (40, 137), (40, 145), (40, 153),\n",
|
||||
" (40, 161), (40, 169), (32, 169), (32, 161), (32, 153), (32, 145),\n",
|
||||
" (32, 137), (32, 129), (32, 121), (32, 113), (40, 113), (56, 113),\n",
|
||||
" (56, 105), (48, 99), (40, 99), (32, 97), (32, 89), (24, 89),\n",
|
||||
" (16, 97), (16, 109), (8, 109), (8, 97), (8, 89), (8, 81),\n",
|
||||
" (8, 73), (8, 65), (8, 57), (16, 57), (8, 49), (8, 41),\n",
|
||||
" (24, 45), (32, 41), (32, 49), (32, 57), (32, 65), (32, 73),\n",
|
||||
" (32, 81), (40, 83), (40, 73), (40, 63), (40, 51), (44, 43),\n",
|
||||
" (44, 35), (44, 27), (32, 25), (24, 25), (16, 25), (16, 17),\n",
|
||||
" (24, 17), (32, 17), (44, 11), (56, 9), (56, 17), (56, 25),\n",
|
||||
" (56, 33), (56, 41), (64, 41), (72, 41), (72, 49), (56, 49),\n",
|
||||
" (48, 51), (56, 57), (56, 65), (48, 63), (48, 73), (56, 73),\n",
|
||||
" (56, 81), (48, 83), (56, 89), (56, 97), (104, 97), (104, 105),\n",
|
||||
" (104, 113), (104, 121), (104, 129), (104, 137), (104, 145), (116, 145),\n",
|
||||
" (124, 145), (132, 145), (132, 137), (140, 137), (148, 137), (156, 137),\n",
|
||||
" (164, 137), (172, 125), (172, 117), (172, 109), (172, 101), (172, 93),\n",
|
||||
" (172, 85), (180, 85), (180, 77), (180, 69), (180, 61), (180, 53),\n",
|
||||
" (172, 53), (172, 61), (172, 69), (172, 77), (164, 81), (148, 85),\n",
|
||||
" (124, 85), (124, 93), (124, 109), (124, 125), (124, 117), (124, 101),\n",
|
||||
" (104, 89), (104, 81), (104, 73), (104, 65), (104, 49), (104, 41),\n",
|
||||
" (104, 33), (104, 25), (104, 17), (92, 9), (80, 9), (72, 9),\n",
|
||||
" (64, 21), (72, 25), (80, 25), (80, 25), (80, 41), (88, 49),\n",
|
||||
" (104, 57), (124, 69), (124, 77), (132, 81), (140, 65), (132, 61),\n",
|
||||
" (124, 61), (124, 53), (124, 45), (124, 37), (124, 29), (132, 21),\n",
|
||||
" (124, 21), (120, 9), (128, 9), (136, 9), (148, 9), (162, 9),\n",
|
||||
" (156, 25), (172, 21), (180, 21), (180, 29), (172, 29), (172, 37),\n",
|
||||
" (172, 45), (180, 45), (180, 37), (188, 41), (196, 49), (204, 57),\n",
|
||||
" (212, 65), (220, 73), (228, 69), (228, 77), (236, 77), (236, 69),\n",
|
||||
" (236, 61), (228, 61), (228, 53), (236, 53), (236, 45), (228, 45),\n",
|
||||
" (228, 37), (236, 37), (236, 29), (228, 29), (228, 21), (236, 21),\n",
|
||||
" (252, 21), (260, 29), (260, 37), (260, 45), (260, 53), (260, 61),\n",
|
||||
" (260, 69), (260, 77), (276, 77), (276, 69), (276, 61), (276, 53),\n",
|
||||
" (284, 53), (284, 61), (284, 69), (284, 77), (284, 85), (284, 93),\n",
|
||||
" (284, 101), (288, 109), (280, 109), (276, 101), (276, 93), (276, 85),\n",
|
||||
" (268, 97), (260, 109), (252, 101), (260, 93), (260, 85), (236, 85),\n",
|
||||
" (228, 85), (228, 93), (236, 93), (236, 101), (228, 101), (228, 109),\n",
|
||||
" (228, 117), (228, 125), (220, 125), (212, 117), (204, 109), (196, 101),\n",
|
||||
" (188, 93), (180, 93), (180, 101), (180, 109), (180, 117), (180, 125),\n",
|
||||
" (196, 145), (204, 145), (212, 145), (220, 145), (228, 145), (236, 145),\n",
|
||||
" (246, 141), (252, 125), (260, 129), (280, 133)\n",
|
||||
" ] # yapf: disable\n",
|
||||
" data['num_vehicles'] = 1\n",
|
||||
" data['depot'] = 0\n",
|
||||
" data[\"locations\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" (288, 149), (288, 129), (270, 133), (256, 141), (256, 157), (246, 157),\n",
|
||||
" (236, 169), (228, 169), (228, 161), (220, 169), (212, 169), (204, 169),\n",
|
||||
" (196, 169), (188, 169), (196, 161), (188, 145), (172, 145), (164, 145),\n",
|
||||
" (156, 145), (148, 145), (140, 145), (148, 169), (164, 169), (172, 169),\n",
|
||||
" (156, 169), (140, 169), (132, 169), (124, 169), (116, 161), (104, 153),\n",
|
||||
" (104, 161), (104, 169), (90, 165), (80, 157), (64, 157), (64, 165),\n",
|
||||
" (56, 169), (56, 161), (56, 153), (56, 145), (56, 137), (56, 129),\n",
|
||||
" (56, 121), (40, 121), (40, 129), (40, 137), (40, 145), (40, 153),\n",
|
||||
" (40, 161), (40, 169), (32, 169), (32, 161), (32, 153), (32, 145),\n",
|
||||
" (32, 137), (32, 129), (32, 121), (32, 113), (40, 113), (56, 113),\n",
|
||||
" (56, 105), (48, 99), (40, 99), (32, 97), (32, 89), (24, 89),\n",
|
||||
" (16, 97), (16, 109), (8, 109), (8, 97), (8, 89), (8, 81),\n",
|
||||
" (8, 73), (8, 65), (8, 57), (16, 57), (8, 49), (8, 41),\n",
|
||||
" (24, 45), (32, 41), (32, 49), (32, 57), (32, 65), (32, 73),\n",
|
||||
" (32, 81), (40, 83), (40, 73), (40, 63), (40, 51), (44, 43),\n",
|
||||
" (44, 35), (44, 27), (32, 25), (24, 25), (16, 25), (16, 17),\n",
|
||||
" (24, 17), (32, 17), (44, 11), (56, 9), (56, 17), (56, 25),\n",
|
||||
" (56, 33), (56, 41), (64, 41), (72, 41), (72, 49), (56, 49),\n",
|
||||
" (48, 51), (56, 57), (56, 65), (48, 63), (48, 73), (56, 73),\n",
|
||||
" (56, 81), (48, 83), (56, 89), (56, 97), (104, 97), (104, 105),\n",
|
||||
" (104, 113), (104, 121), (104, 129), (104, 137), (104, 145), (116, 145),\n",
|
||||
" (124, 145), (132, 145), (132, 137), (140, 137), (148, 137), (156, 137),\n",
|
||||
" (164, 137), (172, 125), (172, 117), (172, 109), (172, 101), (172, 93),\n",
|
||||
" (172, 85), (180, 85), (180, 77), (180, 69), (180, 61), (180, 53),\n",
|
||||
" (172, 53), (172, 61), (172, 69), (172, 77), (164, 81), (148, 85),\n",
|
||||
" (124, 85), (124, 93), (124, 109), (124, 125), (124, 117), (124, 101),\n",
|
||||
" (104, 89), (104, 81), (104, 73), (104, 65), (104, 49), (104, 41),\n",
|
||||
" (104, 33), (104, 25), (104, 17), (92, 9), (80, 9), (72, 9),\n",
|
||||
" (64, 21), (72, 25), (80, 25), (80, 25), (80, 41), (88, 49),\n",
|
||||
" (104, 57), (124, 69), (124, 77), (132, 81), (140, 65), (132, 61),\n",
|
||||
" (124, 61), (124, 53), (124, 45), (124, 37), (124, 29), (132, 21),\n",
|
||||
" (124, 21), (120, 9), (128, 9), (136, 9), (148, 9), (162, 9),\n",
|
||||
" (156, 25), (172, 21), (180, 21), (180, 29), (172, 29), (172, 37),\n",
|
||||
" (172, 45), (180, 45), (180, 37), (188, 41), (196, 49), (204, 57),\n",
|
||||
" (212, 65), (220, 73), (228, 69), (228, 77), (236, 77), (236, 69),\n",
|
||||
" (236, 61), (228, 61), (228, 53), (236, 53), (236, 45), (228, 45),\n",
|
||||
" (228, 37), (236, 37), (236, 29), (228, 29), (228, 21), (236, 21),\n",
|
||||
" (252, 21), (260, 29), (260, 37), (260, 45), (260, 53), (260, 61),\n",
|
||||
" (260, 69), (260, 77), (276, 77), (276, 69), (276, 61), (276, 53),\n",
|
||||
" (284, 53), (284, 61), (284, 69), (284, 77), (284, 85), (284, 93),\n",
|
||||
" (284, 101), (288, 109), (280, 109), (276, 101), (276, 93), (276, 85),\n",
|
||||
" (268, 97), (260, 109), (252, 101), (260, 93), (260, 85), (236, 85),\n",
|
||||
" (228, 85), (228, 93), (236, 93), (236, 101), (228, 101), (228, 109),\n",
|
||||
" (228, 117), (228, 125), (220, 125), (212, 117), (204, 109), (196, 101),\n",
|
||||
" (188, 93), (180, 93), (180, 101), (180, 109), (180, 117), (180, 125),\n",
|
||||
" (196, 145), (204, 145), (212, 145), (220, 145), (228, 145), (236, 145),\n",
|
||||
" (246, 141), (252, 125), (260, 129), (280, 133)\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" data[\"num_vehicles\"] = 1\n",
|
||||
" data[\"depot\"] = 0\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -156,26 +158,26 @@
|
||||
" distances[from_counter][to_counter] = 0\n",
|
||||
" else:\n",
|
||||
" # Euclidean distance\n",
|
||||
" distances[from_counter][to_counter] = (int(\n",
|
||||
" math.hypot((from_node[0] - to_node[0]),\n",
|
||||
" (from_node[1] - to_node[1]))))\n",
|
||||
" distances[from_counter][to_counter] = int(\n",
|
||||
" math.hypot((from_node[0] - to_node[0]), (from_node[1] - to_node[1]))\n",
|
||||
" )\n",
|
||||
" return distances\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",
|
||||
" index = routing.Start(0)\n",
|
||||
" plan_output = 'Route:\\n'\n",
|
||||
" plan_output = \"Route:\\n\"\n",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" plan_output += f' {manager.IndexToNode(index)} ->'\n",
|
||||
" plan_output += f\" {manager.IndexToNode(index)} ->\"\n",
|
||||
" previous_index = 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\" {manager.IndexToNode(index)}\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" plan_output += f'Objective: {route_distance}m\\n'\n",
|
||||
" plan_output += f\"Objective: {route_distance}m\\n\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -184,13 +186,14 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['locations']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"locations\"]), data[\"num_vehicles\"], data[\"depot\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
"\n",
|
||||
" distance_matrix = compute_euclidean_distance_matrix(data['locations'])\n",
|
||||
" distance_matrix = compute_euclidean_distance_matrix(data[\"locations\"])\n",
|
||||
"\n",
|
||||
" def distance_callback(from_index, to_index):\n",
|
||||
" \"\"\"Returns the distance between the two nodes.\"\"\"\n",
|
||||
@@ -207,7 +210,8 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" data['distance_matrix'] = [\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" [0, 2451, 713, 1018, 1631, 1374, 2408, 213, 2571, 875, 1420, 2145, 1972],\n",
|
||||
" [2451, 0, 1745, 1524, 831, 1240, 959, 2596, 403, 1589, 1374, 357, 579],\n",
|
||||
" [713, 1745, 0, 355, 920, 803, 1737, 851, 1858, 262, 940, 1453, 1260],\n",
|
||||
@@ -104,26 +104,26 @@
|
||||
" [1420, 1374, 940, 1056, 879, 225, 1891, 1605, 1645, 679, 0, 1017, 1200],\n",
|
||||
" [2145, 357, 1453, 1280, 586, 887, 1114, 2300, 653, 1272, 1017, 0, 504],\n",
|
||||
" [1972, 579, 1260, 987, 371, 999, 701, 2099, 600, 1162, 1200, 504, 0],\n",
|
||||
" ] # yapf: disable\n",
|
||||
" data['num_vehicles'] = 1\n",
|
||||
" data['depot'] = 0\n",
|
||||
" ]\n",
|
||||
" data[\"num_vehicles\"] = 1\n",
|
||||
" data[\"depot\"] = 0\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {solution.ObjectiveValue()} miles')\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()} miles\")\n",
|
||||
" index = routing.Start(0)\n",
|
||||
" plan_output = 'Route for vehicle 0:\\n'\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",
|
||||
" plan_output += f\" {manager.IndexToNode(index)} ->\"\n",
|
||||
" previous_index = 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\" {manager.IndexToNode(index)}\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" plan_output += f'Route distance: {route_distance}miles\\n'\n",
|
||||
" plan_output += f\"Route distance: {route_distance}miles\\n\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -132,8 +132,9 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_matrix\"]), data[\"num_vehicles\"], data[\"depot\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
@@ -144,7 +145,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",
|
||||
@@ -154,7 +155,8 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
|
||||
@@ -90,94 +90,45 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\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",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" ],\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n",
|
||||
" [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n",
|
||||
" [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n",
|
||||
" [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n",
|
||||
" [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n",
|
||||
" [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n",
|
||||
" [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n",
|
||||
" [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n",
|
||||
" [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n",
|
||||
" [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n",
|
||||
" [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n",
|
||||
" [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n",
|
||||
" [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n",
|
||||
" [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n",
|
||||
" [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n",
|
||||
" [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n",
|
||||
" [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" data['num_vehicles'] = 1\n",
|
||||
" data['depot'] = 0\n",
|
||||
" data[\"num_vehicles\"] = 1\n",
|
||||
" data[\"depot\"] = 0\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",
|
||||
" index = routing.Start(0)\n",
|
||||
" plan_output = 'Route for vehicle 0:\\n'\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",
|
||||
" plan_output += f\" {manager.IndexToNode(index)} ->\"\n",
|
||||
" previous_index = 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",
|
||||
" plan_output += f\" {manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -187,20 +138,20 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_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 distance_callback(from_index, to_index):\n",
|
||||
" \"\"\"Returns the distance between the two nodes.\"\"\"\n",
|
||||
" # 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",
|
||||
@@ -210,7 +161,8 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
|
||||
@@ -98,100 +98,52 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\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",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" ],\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n",
|
||||
" [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n",
|
||||
" [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n",
|
||||
" [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n",
|
||||
" [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n",
|
||||
" [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n",
|
||||
" [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n",
|
||||
" [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n",
|
||||
" [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n",
|
||||
" [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n",
|
||||
" [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n",
|
||||
" [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n",
|
||||
" [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n",
|
||||
" [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n",
|
||||
" [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n",
|
||||
" [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n",
|
||||
" [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n",
|
||||
" # fmt: on\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(data, manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {solution.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
" total_distance = 0\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",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" plan_output += f' {manager.IndexToNode(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",
|
||||
" plan_output += f' {manager.IndexToNode(index)}\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" )\n",
|
||||
" plan_output += f\" {manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += route_distance\n",
|
||||
" print(f'Total Distance of all routes: {total_distance}m')\n",
|
||||
" print(f\"Total Distance of all routes: {total_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -201,20 +153,20 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_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 distance_callback(from_index, to_index):\n",
|
||||
" \"\"\"Returns the distance between the two nodes.\"\"\"\n",
|
||||
" # 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",
|
||||
@@ -224,7 +176,8 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
@@ -233,7 +186,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",
|
||||
|
||||
@@ -98,9 +98,9 @@
|
||||
"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,43 +120,45 @@
|
||||
" [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():\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",
|
||||
" 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'\n",
|
||||
" plan_output += f'Time of the route: {solution.Value(time_var)}min\\n'\n",
|
||||
" plan_output += f\"{manager.IndexToNode(index)} \"\n",
|
||||
" plan_output += f\"Time({solution.Value(time_var)})\\n\"\n",
|
||||
" plan_output += f\"Time of the route: {solution.Value(time_var)}min\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_time += solution.Value(time_var)\n",
|
||||
" print(f'Total time of all routes: {total_time}min')\n",
|
||||
" print(f\"Total time of all routes: {total_time}min\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -165,21 +167,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 + service time between the two nodes.\"\"\"\n",
|
||||
" # Convert from routing variable Index to time matrix NodeIndex.\n",
|
||||
" from_node = manager.IndexToNode(from_index)\n",
|
||||
" to_node = manager.IndexToNode(to_index)\n",
|
||||
" return data['time_matrix'][from_node][to_node] + data['service_time'][\n",
|
||||
" from_node]\n",
|
||||
" return data[\"time_matrix\"][from_node][to_node] + data[\"service_time\"][from_node]\n",
|
||||
"\n",
|
||||
" transit_callback_index = routing.RegisterTransitCallback(time_callback)\n",
|
||||
"\n",
|
||||
@@ -187,13 +188,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, # needed optional waiting time to place break\n",
|
||||
" 180, # maximum time per vehicle\n",
|
||||
" True, # Force start cumul to zero.\n",
|
||||
" time)\n",
|
||||
" time,\n",
|
||||
" )\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(time)\n",
|
||||
" time_dimension.SetGlobalSpanCostCoefficient(10)\n",
|
||||
"\n",
|
||||
@@ -202,7 +204,7 @@
|
||||
" 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",
|
||||
" break_intervals = {}\n",
|
||||
" for v in range(manager.GetNumberOfVehicles()):\n",
|
||||
@@ -212,19 +214,21 @@
|
||||
" 60, # start max\n",
|
||||
" 10, # duration: 10 min\n",
|
||||
" False, # optional: no\n",
|
||||
" f'Break for vehicle {v}')\n",
|
||||
" f\"Break for vehicle {v}\",\n",
|
||||
" )\n",
|
||||
" ]\n",
|
||||
" time_dimension.SetBreakIntervalsOfVehicle(\n",
|
||||
" break_intervals[v], # breaks\n",
|
||||
" v, # vehicle index\n",
|
||||
" node_visit_transit)\n",
|
||||
" break_intervals[v], v, node_visit_transit # breaks # vehicle index\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",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" # search_parameters.log_search = True\n",
|
||||
" search_parameters.time_limit.FromSeconds(2)\n",
|
||||
"\n",
|
||||
@@ -235,7 +239,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",
|
||||
|
||||
@@ -90,109 +90,61 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\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",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" ],\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n",
|
||||
" [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n",
|
||||
" [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n",
|
||||
" [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n",
|
||||
" [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n",
|
||||
" [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n",
|
||||
" [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n",
|
||||
" [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n",
|
||||
" [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n",
|
||||
" [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n",
|
||||
" [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n",
|
||||
" [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n",
|
||||
" [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n",
|
||||
" [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n",
|
||||
" [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n",
|
||||
" [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n",
|
||||
" [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" data['demands'] = [0, 1, 1, 2, 4, 2, 4, 8, 8, 1, 2, 1, 2, 4, 4, 8, 8]\n",
|
||||
" data['vehicle_capacities'] = [15, 15, 15, 15]\n",
|
||||
" data['num_vehicles'] = 4\n",
|
||||
" data['depot'] = 0\n",
|
||||
" data[\"demands\"] = [0, 1, 1, 2, 4, 2, 4, 8, 8, 1, 2, 1, 2, 4, 4, 8, 8]\n",
|
||||
" data[\"vehicle_capacities\"] = [15, 15, 15, 15]\n",
|
||||
" data[\"num_vehicles\"] = 4\n",
|
||||
" data[\"depot\"] = 0\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(data, manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {solution.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
" total_distance = 0\n",
|
||||
" total_load = 0\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",
|
||||
" route_distance = 0\n",
|
||||
" route_load = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" node_index = manager.IndexToNode(index)\n",
|
||||
" route_load += data['demands'][node_index]\n",
|
||||
" plan_output += f' {node_index} Load({route_load}) -> '\n",
|
||||
" route_load += data[\"demands\"][node_index]\n",
|
||||
" plan_output += f\" {node_index} Load({route_load}) -> \"\n",
|
||||
" previous_index = index\n",
|
||||
" index = solution.Value(routing.NextVar(index))\n",
|
||||
" route_distance += routing.GetArcCostForVehicle(\n",
|
||||
" previous_index, index, vehicle_id)\n",
|
||||
" plan_output += f' {manager.IndexToNode(index)} Load({route_load})\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" plan_output += f'Load of the route: {route_load}\\n'\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" )\n",
|
||||
" plan_output += f\" {manager.IndexToNode(index)} Load({route_load})\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" plan_output += f\"Load of the route: {route_load}\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += route_distance\n",
|
||||
" total_load += route_load\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 distance of all routes: {total_distance}m\")\n",
|
||||
" print(f\"Total load of all routes: {total_load}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -201,49 +153,50 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_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 distance_callback(from_index, to_index):\n",
|
||||
" \"\"\"Returns the distance between the two nodes.\"\"\"\n",
|
||||
" # 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",
|
||||
" # Define cost of each arc.\n",
|
||||
" routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" # Add Capacity constraint.\n",
|
||||
" def demand_callback(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['demands'][from_node]\n",
|
||||
" return data[\"demands\"][from_node]\n",
|
||||
"\n",
|
||||
" demand_callback_index = routing.RegisterUnaryTransitCallback(\n",
|
||||
" demand_callback)\n",
|
||||
" demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback)\n",
|
||||
" routing.AddDimensionWithVehicleCapacity(\n",
|
||||
" demand_callback_index,\n",
|
||||
" 0, # null capacity slack\n",
|
||||
" data['vehicle_capacities'], # vehicle maximum capacities\n",
|
||||
" data[\"vehicle_capacities\"], # vehicle maximum capacities\n",
|
||||
" True, # start cumul to zero\n",
|
||||
" 'Capacity')\n",
|
||||
" \"Capacity\",\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",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.time_limit.FromSeconds(1)\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -90,118 +90,70 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\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",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" ],\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n",
|
||||
" [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n",
|
||||
" [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n",
|
||||
" [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n",
|
||||
" [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n",
|
||||
" [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n",
|
||||
" [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n",
|
||||
" [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n",
|
||||
" [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n",
|
||||
" [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n",
|
||||
" [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n",
|
||||
" [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n",
|
||||
" [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n",
|
||||
" [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n",
|
||||
" [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n",
|
||||
" [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n",
|
||||
" [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" data['demands'] = [0, 1, 1, 3, 6, 3, 6, 8, 8, 1, 2, 1, 2, 6, 6, 8, 8]\n",
|
||||
" data['vehicle_capacities'] = [15, 15, 15, 15]\n",
|
||||
" data['num_vehicles'] = 4\n",
|
||||
" data['depot'] = 0\n",
|
||||
" data[\"demands\"] = [0, 1, 1, 3, 6, 3, 6, 8, 8, 1, 2, 1, 2, 6, 6, 8, 8]\n",
|
||||
" data[\"vehicle_capacities\"] = [15, 15, 15, 15]\n",
|
||||
" data[\"num_vehicles\"] = 4\n",
|
||||
" data[\"depot\"] = 0\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",
|
||||
" total_load = 0\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",
|
||||
" route_distance = 0\n",
|
||||
" route_load = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" node_index = manager.IndexToNode(index)\n",
|
||||
" route_load += data['demands'][node_index]\n",
|
||||
" plan_output += f' {node_index} Load({route_load}) -> '\n",
|
||||
" route_load += data[\"demands\"][node_index]\n",
|
||||
" plan_output += f\" {node_index} Load({route_load}) -> \"\n",
|
||||
" previous_index = index\n",
|
||||
" index = assignment.Value(routing.NextVar(index))\n",
|
||||
" route_distance += routing.GetArcCostForVehicle(\n",
|
||||
" previous_index, index, vehicle_id)\n",
|
||||
" plan_output += f' {manager.IndexToNode(index)} Load({route_load})\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" plan_output += f'Load of the route: {route_load}\\n'\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" )\n",
|
||||
" plan_output += f\" {manager.IndexToNode(index)} Load({route_load})\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" plan_output += f\"Load of the route: {route_load}\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += route_distance\n",
|
||||
" total_load += route_load\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 Distance of all routes: {total_distance}m\")\n",
|
||||
" print(f\"Total Load of all routes: {total_load}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -210,53 +162,54 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_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 distance_callback(from_index, to_index):\n",
|
||||
" \"\"\"Returns the distance between the two nodes.\"\"\"\n",
|
||||
" # 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",
|
||||
" # Define cost of each arc.\n",
|
||||
" routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" # Add Capacity constraint.\n",
|
||||
" def demand_callback(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['demands'][from_node]\n",
|
||||
" return data[\"demands\"][from_node]\n",
|
||||
"\n",
|
||||
" demand_callback_index = routing.RegisterUnaryTransitCallback(\n",
|
||||
" demand_callback)\n",
|
||||
" demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback)\n",
|
||||
" routing.AddDimensionWithVehicleCapacity(\n",
|
||||
" demand_callback_index,\n",
|
||||
" 0, # null capacity slack\n",
|
||||
" data['vehicle_capacities'], # vehicle maximum capacities\n",
|
||||
" data[\"vehicle_capacities\"], # vehicle maximum capacities\n",
|
||||
" True, # start cumul to zero\n",
|
||||
" 'Capacity')\n",
|
||||
" \"Capacity\",\n",
|
||||
" )\n",
|
||||
" # Allow to drop nodes.\n",
|
||||
" penalty = 1000\n",
|
||||
" for node in range(1, len(data['distance_matrix'])):\n",
|
||||
" for node in range(1, len(data[\"distance_matrix\"])):\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",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.time_limit.FromSeconds(1)\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
|
||||
@@ -98,100 +98,52 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\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",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" ],\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n",
|
||||
" [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n",
|
||||
" [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n",
|
||||
" [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n",
|
||||
" [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n",
|
||||
" [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n",
|
||||
" [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n",
|
||||
" [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n",
|
||||
" [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n",
|
||||
" [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n",
|
||||
" [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n",
|
||||
" [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n",
|
||||
" [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n",
|
||||
" [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n",
|
||||
" [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n",
|
||||
" [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n",
|
||||
" [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n",
|
||||
" # fmt: on\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(data, manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {solution.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
" max_route_distance = 0\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",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" plan_output += f' {manager.IndexToNode(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",
|
||||
" plan_output += f'{manager.IndexToNode(index)}\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" )\n",
|
||||
" plan_output += f\"{manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" max_route_distance = max(route_distance, max_route_distance)\n",
|
||||
" print(f'Maximum of the route distances: {max_route_distance}m')\n",
|
||||
" print(f\"Maximum of the route distances: {max_route_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -201,20 +153,20 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_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 distance_callback(from_index, to_index):\n",
|
||||
" \"\"\"Returns the distance between the two nodes.\"\"\"\n",
|
||||
" # 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",
|
||||
@@ -222,20 +174,22 @@
|
||||
" 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",
|
||||
" 3000, # 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",
|
||||
" distance_dimension.SetGlobalSpanCostCoefficient(100)\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",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
@@ -244,7 +198,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",
|
||||
|
||||
@@ -90,106 +90,58 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\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",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" ],\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n",
|
||||
" [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n",
|
||||
" [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n",
|
||||
" [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n",
|
||||
" [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n",
|
||||
" [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n",
|
||||
" [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n",
|
||||
" [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n",
|
||||
" [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n",
|
||||
" [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n",
|
||||
" [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n",
|
||||
" [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n",
|
||||
" [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n",
|
||||
" [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n",
|
||||
" [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n",
|
||||
" [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n",
|
||||
" [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" data['initial_routes'] = [\n",
|
||||
" data[\"initial_routes\"] = [\n",
|
||||
" [8, 16, 14, 13, 12, 11],\n",
|
||||
" [3, 4, 9, 10],\n",
|
||||
" [15, 1],\n",
|
||||
" [7, 5, 2, 6],\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(data, manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {solution.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
" max_route_distance = 0\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",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" plan_output += f' {manager.IndexToNode(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",
|
||||
" plan_output += f'{manager.IndexToNode(index)}\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" )\n",
|
||||
" plan_output += f\"{manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" max_route_distance = max(route_distance, max_route_distance)\n",
|
||||
" print(f'Maximum of the route distances: {max_route_distance}m')\n",
|
||||
" print(f\"Maximum of the route distances: {max_route_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -199,20 +151,20 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_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 distance_callback(from_index, to_index):\n",
|
||||
" \"\"\"Returns the distance between the two nodes.\"\"\"\n",
|
||||
" # 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",
|
||||
@@ -220,13 +172,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",
|
||||
" 3000, # 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",
|
||||
" distance_dimension.SetGlobalSpanCostCoefficient(100)\n",
|
||||
"\n",
|
||||
@@ -242,18 +195,18 @@
|
||||
" routing.CloseModelWithParameters(search_parameters)\n",
|
||||
"\n",
|
||||
" # Get initial solution from routes after closing the model.\n",
|
||||
" initial_solution = routing.ReadAssignmentFromRoutes(data['initial_routes'],\n",
|
||||
" True)\n",
|
||||
" print('Initial solution:')\n",
|
||||
" initial_solution = routing.ReadAssignmentFromRoutes(data[\"initial_routes\"], True)\n",
|
||||
" print(\"Initial solution:\")\n",
|
||||
" print_solution(data, manager, routing, initial_solution)\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveFromAssignmentWithParameters(\n",
|
||||
" initial_solution, search_parameters)\n",
|
||||
" initial_solution, search_parameters\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Print solution on console.\n",
|
||||
" if solution:\n",
|
||||
" print('Solution after search:')\n",
|
||||
" print(\"Solution after search:\")\n",
|
||||
" print_solution(data, manager, routing, solution)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -94,77 +94,28 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\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",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" ],\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n",
|
||||
" [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n",
|
||||
" [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n",
|
||||
" [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n",
|
||||
" [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n",
|
||||
" [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n",
|
||||
" [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n",
|
||||
" [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n",
|
||||
" [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n",
|
||||
" [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n",
|
||||
" [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n",
|
||||
" [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n",
|
||||
" [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n",
|
||||
" [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n",
|
||||
" [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n",
|
||||
" [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n",
|
||||
" [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" data['value'] = [\n",
|
||||
" data[\"value\"] = [\n",
|
||||
" 0, # depot\n",
|
||||
" 42, # 1\n",
|
||||
" 42, # 2\n",
|
||||
@@ -183,23 +134,23 @@
|
||||
" 42, # 15\n",
|
||||
" 42, # 16\n",
|
||||
" ]\n",
|
||||
" assert len(data['distance_matrix']) == len(data['value'])\n",
|
||||
" data['num_vehicles'] = 4\n",
|
||||
" data['depot'] = 0\n",
|
||||
" assert len(data[\"distance_matrix\"]) == len(data[\"value\"])\n",
|
||||
" data[\"num_vehicles\"] = 4\n",
|
||||
" data[\"depot\"] = 0\n",
|
||||
" 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",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
" max_route_distance = 0\n",
|
||||
" dim_one = routing.GetDimensionOrDie('One')\n",
|
||||
" dim_two = routing.GetDimensionOrDie('Two')\n",
|
||||
" dim_one = routing.GetDimensionOrDie(\"One\")\n",
|
||||
" dim_two = routing.GetDimensionOrDie(\"Two\")\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",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" one_var = dim_one.CumulVar(index)\n",
|
||||
@@ -207,23 +158,27 @@
|
||||
" two_var = dim_two.CumulVar(index)\n",
|
||||
" two_slack_var = dim_two.SlackVar(index)\n",
|
||||
" plan_output += (\n",
|
||||
" f' N:{manager.IndexToNode(index)}'\n",
|
||||
" f' one:({solution.Value(one_var)}, {solution.Value(one_slack_var)})'\n",
|
||||
" f' two:({solution.Value(two_var)}, {solution.Value(two_slack_var)})'\n",
|
||||
" ' -> ')\n",
|
||||
" f\" N:{manager.IndexToNode(index)}\"\n",
|
||||
" f\" one:({solution.Value(one_var)}, {solution.Value(one_slack_var)})\"\n",
|
||||
" f\" two:({solution.Value(two_var)}, {solution.Value(two_slack_var)})\"\n",
|
||||
" \" -> \"\n",
|
||||
" )\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_id\n",
|
||||
" )\n",
|
||||
" one_var = dim_one.CumulVar(index)\n",
|
||||
" two_var = dim_two.CumulVar(index)\n",
|
||||
" plan_output += (f'N:{manager.IndexToNode(index)}'\n",
|
||||
" f' one:{solution.Value(one_var)}'\n",
|
||||
" f' two:{solution.Value(two_var)}\\n')\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" plan_output += (\n",
|
||||
" f\"N:{manager.IndexToNode(index)}\"\n",
|
||||
" f\" one:{solution.Value(one_var)}\"\n",
|
||||
" f\" two:{solution.Value(two_var)}\\n\"\n",
|
||||
" )\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" max_route_distance = max(route_distance, max_route_distance)\n",
|
||||
" print(f'Maximum of the route distances: {max_route_distance}m')\n",
|
||||
" print(f\"Maximum of the route distances: {max_route_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -233,8 +188,9 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_matrix\"]), data[\"num_vehicles\"], data[\"depot\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
@@ -246,7 +202,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",
|
||||
@@ -254,13 +210,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",
|
||||
" 3_000, # 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",
|
||||
" distance_dimension.SetGlobalSpanCostCoefficient(10)\n",
|
||||
"\n",
|
||||
@@ -272,8 +229,9 @@
|
||||
" 42 * 16, # capacity: be able to store PEAK*ROUTE_LENGTH in worst case\n",
|
||||
" 42, # slack_max: to be able to store peak in slack\n",
|
||||
" True, # Fix StartCumulToZero not really matter here\n",
|
||||
" 'One')\n",
|
||||
" dim_one = routing.GetDimensionOrDie('One')\n",
|
||||
" \"One\",\n",
|
||||
" )\n",
|
||||
" dim_one = routing.GetDimensionOrDie(\"One\")\n",
|
||||
"\n",
|
||||
" # Dimension Two will be used to store the max node value in the route end node\n",
|
||||
" # CumulVar so we can use it as an objective cost.\n",
|
||||
@@ -282,16 +240,17 @@
|
||||
" 42 * 16, # capacity: be able to have PEAK value in CumulVar(End)\n",
|
||||
" 42, # slack_max: to be able to store peak in slack\n",
|
||||
" True, # Fix StartCumulToZero YES here\n",
|
||||
" 'Two')\n",
|
||||
" dim_two = routing.GetDimensionOrDie('Two')\n",
|
||||
" \"Two\",\n",
|
||||
" )\n",
|
||||
" dim_two = routing.GetDimensionOrDie(\"Two\")\n",
|
||||
"\n",
|
||||
" # force depot Slack to be value since we don't have any predecessor...\n",
|
||||
" for v in range(manager.GetNumberOfVehicles()):\n",
|
||||
" start = routing.Start(v)\n",
|
||||
" dim_one.SlackVar(start).SetValue(data['value'][0])\n",
|
||||
" dim_one.SlackVar(start).SetValue(data[\"value\"][0])\n",
|
||||
" routing.AddToAssignment(dim_one.SlackVar(start))\n",
|
||||
"\n",
|
||||
" dim_two.SlackVar(start).SetValue(data['value'][0])\n",
|
||||
" dim_two.SlackVar(start).SetValue(data[\"value\"][0])\n",
|
||||
" routing.AddToAssignment(dim_two.SlackVar(start))\n",
|
||||
"\n",
|
||||
" # Step by step relation\n",
|
||||
@@ -305,14 +264,12 @@
|
||||
" for v in range(manager.GetNumberOfVehicles()):\n",
|
||||
" previous_index = routing.Start(v)\n",
|
||||
" cond = routing.NextVar(previous_index) == index\n",
|
||||
" value = solver.Max(dim_one.SlackVar(previous_index),\n",
|
||||
" data['value'][node])\n",
|
||||
" value = solver.Max(dim_one.SlackVar(previous_index), data[\"value\"][node])\n",
|
||||
" test.append((cond * value).Var())\n",
|
||||
" for previous in range(1, 17):\n",
|
||||
" previous_index = manager.NodeToIndex(previous)\n",
|
||||
" cond = routing.NextVar(previous_index) == index\n",
|
||||
" value = solver.Max(dim_one.SlackVar(previous_index),\n",
|
||||
" data['value'][node])\n",
|
||||
" value = solver.Max(dim_one.SlackVar(previous_index), data[\"value\"][node])\n",
|
||||
" test.append((cond * value).Var())\n",
|
||||
" solver.Add(solver.Sum(test) == dim_one.SlackVar(index))\n",
|
||||
"\n",
|
||||
@@ -335,9 +292,11 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" # search_parameters.log_search = True\n",
|
||||
" search_parameters.time_limit.FromSeconds(5)\n",
|
||||
"\n",
|
||||
@@ -348,7 +307,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",
|
||||
|
||||
@@ -90,77 +90,28 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\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",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" ],\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n",
|
||||
" [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n",
|
||||
" [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n",
|
||||
" [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n",
|
||||
" [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n",
|
||||
" [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n",
|
||||
" [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n",
|
||||
" [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n",
|
||||
" [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n",
|
||||
" [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n",
|
||||
" [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n",
|
||||
" [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n",
|
||||
" [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n",
|
||||
" [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n",
|
||||
" [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n",
|
||||
" [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n",
|
||||
" [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" data['pickups_deliveries'] = [\n",
|
||||
" data[\"pickups_deliveries\"] = [\n",
|
||||
" [1, 6],\n",
|
||||
" [2, 10],\n",
|
||||
" [4, 3],\n",
|
||||
@@ -170,30 +121,31 @@
|
||||
" [13, 12],\n",
|
||||
" [16, 14],\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(data, manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {solution.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
" total_distance = 0\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",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" plan_output += f' {manager.IndexToNode(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",
|
||||
" plan_output += f'{manager.IndexToNode(index)}\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" )\n",
|
||||
" plan_output += f\"{manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += route_distance\n",
|
||||
" print(f'Total Distance of all routes: {total_distance}m')\n",
|
||||
" print(f\"Total Distance of all routes: {total_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -202,8 +154,9 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_matrix\"]), data[\"num_vehicles\"], data[\"depot\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
@@ -215,38 +168,41 @@
|
||||
" # 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",
|
||||
" 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",
|
||||
" 3000, # 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",
|
||||
" distance_dimension.SetGlobalSpanCostCoefficient(100)\n",
|
||||
"\n",
|
||||
" # Define Transportation Requests.\n",
|
||||
" for request in data['pickups_deliveries']:\n",
|
||||
" for request in data[\"pickups_deliveries\"]:\n",
|
||||
" pickup_index = manager.NodeToIndex(request[0])\n",
|
||||
" delivery_index = manager.NodeToIndex(request[1])\n",
|
||||
" routing.AddPickupAndDelivery(pickup_index, delivery_index)\n",
|
||||
" routing.solver().Add(\n",
|
||||
" routing.VehicleVar(pickup_index) == routing.VehicleVar(\n",
|
||||
" delivery_index))\n",
|
||||
" routing.VehicleVar(pickup_index) == routing.VehicleVar(delivery_index)\n",
|
||||
" )\n",
|
||||
" routing.solver().Add(\n",
|
||||
" distance_dimension.CumulVar(pickup_index) <=\n",
|
||||
" distance_dimension.CumulVar(delivery_index))\n",
|
||||
" distance_dimension.CumulVar(pickup_index)\n",
|
||||
" <= distance_dimension.CumulVar(delivery_index)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION)\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
|
||||
@@ -90,77 +90,28 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\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",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" ],\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n",
|
||||
" [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n",
|
||||
" [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n",
|
||||
" [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n",
|
||||
" [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n",
|
||||
" [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n",
|
||||
" [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n",
|
||||
" [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n",
|
||||
" [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n",
|
||||
" [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n",
|
||||
" [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n",
|
||||
" [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n",
|
||||
" [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n",
|
||||
" [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n",
|
||||
" [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n",
|
||||
" [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n",
|
||||
" [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" data['pickups_deliveries'] = [\n",
|
||||
" data[\"pickups_deliveries\"] = [\n",
|
||||
" [1, 6],\n",
|
||||
" [2, 10],\n",
|
||||
" [4, 3],\n",
|
||||
@@ -170,30 +121,31 @@
|
||||
" [13, 12],\n",
|
||||
" [16, 14],\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(data, manager, routing, assignment):\n",
|
||||
" \"\"\"Prints assignment on console.\"\"\"\n",
|
||||
" print(f'Objective: {assignment.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {assignment.ObjectiveValue()}\")\n",
|
||||
" total_distance = 0\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",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" plan_output += f' {manager.IndexToNode(index)} -> '\n",
|
||||
" plan_output += f\" {manager.IndexToNode(index)} -> \"\n",
|
||||
" previous_index = index\n",
|
||||
" index = assignment.Value(routing.NextVar(index))\n",
|
||||
" route_distance += routing.GetArcCostForVehicle(\n",
|
||||
" previous_index, index, vehicle_id)\n",
|
||||
" plan_output += f'{manager.IndexToNode(index)}\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" )\n",
|
||||
" plan_output += f\"{manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += route_distance\n",
|
||||
" print(f'Total Distance of all routes: {total_distance}m')\n",
|
||||
" print(f\"Total Distance of all routes: {total_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -202,8 +154,9 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_matrix\"]), data[\"num_vehicles\"], data[\"depot\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
@@ -215,40 +168,44 @@
|
||||
" # 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",
|
||||
" 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",
|
||||
" 3000, # 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",
|
||||
" distance_dimension.SetGlobalSpanCostCoefficient(100)\n",
|
||||
"\n",
|
||||
" # Define Transportation Requests.\n",
|
||||
" for request in data['pickups_deliveries']:\n",
|
||||
" for request in data[\"pickups_deliveries\"]:\n",
|
||||
" pickup_index = manager.NodeToIndex(request[0])\n",
|
||||
" delivery_index = manager.NodeToIndex(request[1])\n",
|
||||
" routing.AddPickupAndDelivery(pickup_index, delivery_index)\n",
|
||||
" routing.solver().Add(\n",
|
||||
" routing.VehicleVar(pickup_index) == routing.VehicleVar(\n",
|
||||
" delivery_index))\n",
|
||||
" routing.VehicleVar(pickup_index) == routing.VehicleVar(delivery_index)\n",
|
||||
" )\n",
|
||||
" routing.solver().Add(\n",
|
||||
" distance_dimension.CumulVar(pickup_index) <=\n",
|
||||
" distance_dimension.CumulVar(delivery_index))\n",
|
||||
" distance_dimension.CumulVar(pickup_index)\n",
|
||||
" <= distance_dimension.CumulVar(delivery_index)\n",
|
||||
" )\n",
|
||||
" routing.SetPickupAndDeliveryPolicyOfAllVehicles(\n",
|
||||
" pywrapcp.RoutingModel.PICKUP_AND_DELIVERY_FIFO)\n",
|
||||
" pywrapcp.RoutingModel.PICKUP_AND_DELIVERY_FIFO\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION)\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" assignment = routing.SolveWithParameters(search_parameters)\n",
|
||||
|
||||
@@ -90,77 +90,28 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\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",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" ],\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n",
|
||||
" [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n",
|
||||
" [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n",
|
||||
" [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n",
|
||||
" [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n",
|
||||
" [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n",
|
||||
" [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n",
|
||||
" [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n",
|
||||
" [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n",
|
||||
" [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n",
|
||||
" [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n",
|
||||
" [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n",
|
||||
" [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n",
|
||||
" [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n",
|
||||
" [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n",
|
||||
" [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n",
|
||||
" [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" data['pickups_deliveries'] = [\n",
|
||||
" data[\"pickups_deliveries\"] = [\n",
|
||||
" [1, 6],\n",
|
||||
" [2, 10],\n",
|
||||
" [4, 3],\n",
|
||||
@@ -170,30 +121,31 @@
|
||||
" [13, 12],\n",
|
||||
" [16, 14],\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(data, manager, routing, assignment):\n",
|
||||
" \"\"\"Prints assignment on console.\"\"\"\n",
|
||||
" print(f'Objective: {assignment.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {assignment.ObjectiveValue()}\")\n",
|
||||
" total_distance = 0\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",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" plan_output += f' {manager.IndexToNode(index)} -> '\n",
|
||||
" plan_output += f\" {manager.IndexToNode(index)} -> \"\n",
|
||||
" previous_index = index\n",
|
||||
" index = assignment.Value(routing.NextVar(index))\n",
|
||||
" route_distance += routing.GetArcCostForVehicle(\n",
|
||||
" previous_index, index, vehicle_id)\n",
|
||||
" plan_output += f'{manager.IndexToNode(index)}\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" )\n",
|
||||
" plan_output += f\"{manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += route_distance\n",
|
||||
" print(f'Total Distance of all routes: {total_distance}m')\n",
|
||||
" print(f\"Total Distance of all routes: {total_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -202,8 +154,9 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_matrix\"]), data[\"num_vehicles\"], data[\"depot\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
@@ -215,40 +168,44 @@
|
||||
" # 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",
|
||||
" 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",
|
||||
" 3000, # 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",
|
||||
" distance_dimension.SetGlobalSpanCostCoefficient(100)\n",
|
||||
"\n",
|
||||
" # Define Transportation Requests.\n",
|
||||
" for request in data['pickups_deliveries']:\n",
|
||||
" for request in data[\"pickups_deliveries\"]:\n",
|
||||
" pickup_index = manager.NodeToIndex(request[0])\n",
|
||||
" delivery_index = manager.NodeToIndex(request[1])\n",
|
||||
" routing.AddPickupAndDelivery(pickup_index, delivery_index)\n",
|
||||
" routing.solver().Add(\n",
|
||||
" routing.VehicleVar(pickup_index) == routing.VehicleVar(\n",
|
||||
" delivery_index))\n",
|
||||
" routing.VehicleVar(pickup_index) == routing.VehicleVar(delivery_index)\n",
|
||||
" )\n",
|
||||
" routing.solver().Add(\n",
|
||||
" distance_dimension.CumulVar(pickup_index) <=\n",
|
||||
" distance_dimension.CumulVar(delivery_index))\n",
|
||||
" distance_dimension.CumulVar(pickup_index)\n",
|
||||
" <= distance_dimension.CumulVar(delivery_index)\n",
|
||||
" )\n",
|
||||
" routing.SetPickupAndDeliveryPolicyOfAllVehicles(\n",
|
||||
" pywrapcp.RoutingModel.PICKUP_AND_DELIVERY_LIFO)\n",
|
||||
" pywrapcp.RoutingModel.PICKUP_AND_DELIVERY_LIFO\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION)\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" assignment = routing.SolveWithParameters(search_parameters)\n",
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
"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",
|
||||
@@ -109,7 +109,7 @@
|
||||
" [9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9],\n",
|
||||
" [7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0],\n",
|
||||
" ]\n",
|
||||
" data['time_windows'] = [\n",
|
||||
" data[\"time_windows\"] = [\n",
|
||||
" (0, 5), # depot\n",
|
||||
" (7, 12), # 1\n",
|
||||
" (10, 15), # 2\n",
|
||||
@@ -128,37 +128,39 @@
|
||||
" (10, 15), # 15\n",
|
||||
" (5, 15), # 16\n",
|
||||
" ]\n",
|
||||
" data['num_vehicles'] = 4\n",
|
||||
" data['vehicle_load_time'] = 5\n",
|
||||
" data['vehicle_unload_time'] = 5\n",
|
||||
" data['depot_capacity'] = 2\n",
|
||||
" data['depot'] = 0\n",
|
||||
" data[\"num_vehicles\"] = 4\n",
|
||||
" data[\"vehicle_load_time\"] = 5\n",
|
||||
" data[\"vehicle_unload_time\"] = 5\n",
|
||||
" data[\"depot_capacity\"] = 2\n",
|
||||
" data[\"depot\"] = 0\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(data, manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {solution.ObjectiveValue()}')\n",
|
||||
" time_dimension = routing.GetDimensionOrDie('Time')\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(\"Time\")\n",
|
||||
" total_time = 0\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",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" time_var = time_dimension.CumulVar(index)\n",
|
||||
" plan_output += (\n",
|
||||
" f'{manager.IndexToNode(index)}'\n",
|
||||
" f' Time({solution.Min(time_var)}, {solution.Max(time_var)})'\n",
|
||||
" ' -> ')\n",
|
||||
" f\"{manager.IndexToNode(index)}\"\n",
|
||||
" f\" Time({solution.Min(time_var)}, {solution.Max(time_var)})\"\n",
|
||||
" \" -> \"\n",
|
||||
" )\n",
|
||||
" index = solution.Value(routing.NextVar(index))\n",
|
||||
" time_var = time_dimension.CumulVar(index)\n",
|
||||
" plan_output += (\n",
|
||||
" f'{manager.IndexToNode(index)}'\n",
|
||||
" f' Time({solution.Min(time_var)},{solution.Max(time_var)})\\n')\n",
|
||||
" plan_output += f'Time of the route: {solution.Min(time_var)}min\\n'\n",
|
||||
" f\"{manager.IndexToNode(index)}\"\n",
|
||||
" f\" Time({solution.Min(time_var)},{solution.Max(time_var)})\\n\"\n",
|
||||
" )\n",
|
||||
" plan_output += f\"Time of the route: {solution.Min(time_var)}min\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_time += solution.Min(time_var)\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",
|
||||
@@ -167,20 +169,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",
|
||||
@@ -188,57 +190,66 @@
|
||||
" 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",
|
||||
" 60, # allow waiting time\n",
|
||||
" 60, # 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",
|
||||
" # Add time window constraints for each location except depot.\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",
|
||||
" time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])\n",
|
||||
" # Add time window constraints for each vehicle start node.\n",
|
||||
" for vehicle_id in range(data['num_vehicles']):\n",
|
||||
" 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",
|
||||
"\n",
|
||||
" # Add resource constraints at the depot.\n",
|
||||
" solver = routing.solver()\n",
|
||||
" intervals = []\n",
|
||||
" for i in range(data['num_vehicles']):\n",
|
||||
" for i in range(data[\"num_vehicles\"]):\n",
|
||||
" # Add time windows at start of routes\n",
|
||||
" intervals.append(\n",
|
||||
" solver.FixedDurationIntervalVar(\n",
|
||||
" time_dimension.CumulVar(routing.Start(i)),\n",
|
||||
" data['vehicle_load_time'], 'depot_interval'))\n",
|
||||
" data[\"vehicle_load_time\"],\n",
|
||||
" \"depot_interval\",\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
" # Add time windows at end of routes.\n",
|
||||
" intervals.append(\n",
|
||||
" solver.FixedDurationIntervalVar(\n",
|
||||
" time_dimension.CumulVar(routing.End(i)),\n",
|
||||
" data['vehicle_unload_time'], 'depot_interval'))\n",
|
||||
" data[\"vehicle_unload_time\"],\n",
|
||||
" \"depot_interval\",\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" depot_usage = [1 for i in range(len(intervals))]\n",
|
||||
" solver.Add(\n",
|
||||
" solver.Cumulative(intervals, depot_usage, data['depot_capacity'],\n",
|
||||
" 'depot'))\n",
|
||||
" solver.Cumulative(intervals, depot_usage, data[\"depot_capacity\"], \"depot\")\n",
|
||||
" )\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",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
@@ -247,7 +258,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",
|
||||
|
||||
@@ -98,109 +98,66 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\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",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" ],\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n",
|
||||
" [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n",
|
||||
" [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n",
|
||||
" [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n",
|
||||
" [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n",
|
||||
" [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n",
|
||||
" [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n",
|
||||
" [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n",
|
||||
" [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n",
|
||||
" [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n",
|
||||
" [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n",
|
||||
" [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n",
|
||||
" [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n",
|
||||
" [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n",
|
||||
" [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n",
|
||||
" [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n",
|
||||
" [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n",
|
||||
" # fmt: on\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(routing_manager: pywrapcp.RoutingIndexManager,\n",
|
||||
" routing_model: pywrapcp.RoutingModel):\n",
|
||||
"def print_solution(\n",
|
||||
" routing_manager: pywrapcp.RoutingIndexManager, routing_model: pywrapcp.RoutingModel\n",
|
||||
"):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print('################')\n",
|
||||
" print(f'Solution objective: {routing_model.CostVar().Value()}')\n",
|
||||
" print(\"################\")\n",
|
||||
" print(f\"Solution objective: {routing_model.CostVar().Value()}\")\n",
|
||||
" total_distance = 0\n",
|
||||
" for vehicle_id in range(routing_manager.GetNumberOfVehicles()):\n",
|
||||
" index = routing_model.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",
|
||||
" while not routing_model.IsEnd(index):\n",
|
||||
" plan_output += f' {routing_manager.IndexToNode(index)} ->'\n",
|
||||
" plan_output += f\" {routing_manager.IndexToNode(index)} ->\"\n",
|
||||
" previous_index = index\n",
|
||||
" index = routing_model.NextVar(index).Value()\n",
|
||||
" route_distance += routing_model.GetArcCostForVehicle(\n",
|
||||
" previous_index, index, vehicle_id)\n",
|
||||
" plan_output += f' {routing_manager.IndexToNode(index)}\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" )\n",
|
||||
" plan_output += f\" {routing_manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += route_distance\n",
|
||||
" print(f'Total Distance of all routes: {total_distance}m')\n",
|
||||
" print(f\"Total Distance of all routes: {total_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class SolutionCallback:\n",
|
||||
" \"\"\"Create a solution callback.\"\"\"\n",
|
||||
"\n",
|
||||
" def __init__(self, manager: pywrapcp.RoutingIndexManager,\n",
|
||||
" model: pywrapcp.RoutingModel, limit: int):\n",
|
||||
" def __init__(\n",
|
||||
" self,\n",
|
||||
" manager: pywrapcp.RoutingIndexManager,\n",
|
||||
" model: pywrapcp.RoutingModel,\n",
|
||||
" limit: int,\n",
|
||||
" ):\n",
|
||||
" self._routing_manager = manager\n",
|
||||
" self._routing_model = model\n",
|
||||
" self._counter = 0\n",
|
||||
@@ -223,9 +180,9 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" routing_manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),\n",
|
||||
" data['num_vehicles'],\n",
|
||||
" data['depot'])\n",
|
||||
" routing_manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"distance_matrix\"]), data[\"num_vehicles\"], data[\"depot\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing_model = pywrapcp.RoutingModel(routing_manager)\n",
|
||||
@@ -237,22 +194,22 @@
|
||||
" # Convert from routing variable Index to distance matrix NodeIndex.\n",
|
||||
" from_node = routing_manager.IndexToNode(from_index)\n",
|
||||
" to_node = routing_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_model.RegisterTransitCallback(\n",
|
||||
" distance_callback)\n",
|
||||
" transit_callback_index = routing_model.RegisterTransitCallback(distance_callback)\n",
|
||||
"\n",
|
||||
" # Define cost of each arc.\n",
|
||||
" routing_model.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)\n",
|
||||
"\n",
|
||||
" # Add Distance constraint.\n",
|
||||
" dimension_name = 'Distance'\n",
|
||||
" dimension_name = \"Distance\"\n",
|
||||
" routing_model.AddDimension(\n",
|
||||
" transit_callback_index,\n",
|
||||
" 0, # no slack\n",
|
||||
" 3000, # vehicle maximum travel distance\n",
|
||||
" True, # start cumul to zero\n",
|
||||
" dimension_name)\n",
|
||||
" dimension_name,\n",
|
||||
" )\n",
|
||||
" distance_dimension = routing_model.GetDimensionOrDie(dimension_name)\n",
|
||||
" distance_dimension.SetGlobalSpanCostCoefficient(100)\n",
|
||||
"\n",
|
||||
@@ -263,9 +220,11 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.time_limit.FromSeconds(5)\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
@@ -273,9 +232,9 @@
|
||||
"\n",
|
||||
" # Print solution on console.\n",
|
||||
" if solution:\n",
|
||||
" print(f'Best objective: {solution_callback.objectives[-1]}')\n",
|
||||
" print(f\"Best objective: {solution_callback.objectives[-1]}\")\n",
|
||||
" else:\n",
|
||||
" print('No solution found !')\n",
|
||||
" print(\"No solution found !\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -90,101 +90,53 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\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",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,\n",
|
||||
" 1016, 868, 1210\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,\n",
|
||||
" 1130, 788, 1552, 754\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,\n",
|
||||
" 1164, 560, 1358\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,\n",
|
||||
" 1050, 674, 1244\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,\n",
|
||||
" 514, 1050, 708\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,\n",
|
||||
" 514, 1278, 480\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,\n",
|
||||
" 662, 742, 856\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,\n",
|
||||
" 320, 1084, 514\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,\n",
|
||||
" 274, 810, 468\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,\n",
|
||||
" 730, 388, 1152, 354\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,\n",
|
||||
" 308, 650, 274, 844\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,\n",
|
||||
" 536, 388, 730\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,\n",
|
||||
" 342, 422, 536\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,\n",
|
||||
" 342, 0, 764, 194\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,\n",
|
||||
" 388, 422, 764, 0, 798\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,\n",
|
||||
" 536, 194, 798, 0\n",
|
||||
" ],\n",
|
||||
" data[\"distance_matrix\"] = [\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354, 468, 776, 662],\n",
|
||||
" [548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674, 1016, 868, 1210],\n",
|
||||
" [776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130, 788, 1552, 754],\n",
|
||||
" [696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822, 1164, 560, 1358],\n",
|
||||
" [582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708, 1050, 674, 1244],\n",
|
||||
" [274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628, 514, 1050, 708],\n",
|
||||
" [502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856, 514, 1278, 480],\n",
|
||||
" [194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320, 662, 742, 856],\n",
|
||||
" [308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662, 320, 1084, 514],\n",
|
||||
" [194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388, 274, 810, 468],\n",
|
||||
" [536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730, 388, 1152, 354],\n",
|
||||
" [502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308, 650, 274, 844],\n",
|
||||
" [388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194, 536, 388, 730],\n",
|
||||
" [354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0, 342, 422, 536],\n",
|
||||
" [468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536, 342, 0, 764, 194],\n",
|
||||
" [776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274, 388, 422, 764, 0, 798],\n",
|
||||
" [662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730, 536, 194, 798, 0],\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
" data['num_vehicles'] = 4\n",
|
||||
" data['starts'] = [1, 2, 15, 16]\n",
|
||||
" data['ends'] = [0, 0, 0, 0]\n",
|
||||
" data[\"num_vehicles\"] = 4\n",
|
||||
" data[\"starts\"] = [1, 2, 15, 16]\n",
|
||||
" data[\"ends\"] = [0, 0, 0, 0]\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(data, manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {solution.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
" max_route_distance = 0\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",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" plan_output += f' {manager.IndexToNode(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",
|
||||
" plan_output += f'{manager.IndexToNode(index)}\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" )\n",
|
||||
" plan_output += f\"{manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" max_route_distance = max(route_distance, max_route_distance)\n",
|
||||
" print(f'Maximum of the route distances: {max_route_distance}m')\n",
|
||||
" print(f\"Maximum of the route distances: {max_route_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -193,21 +145,20 @@
|
||||
" 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\"]), data[\"num_vehicles\"], data[\"starts\"], data[\"ends\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # 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",
|
||||
" # 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",
|
||||
@@ -215,20 +166,22 @@
|
||||
" 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",
|
||||
" distance_dimension.SetGlobalSpanCostCoefficient(100)\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",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
"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",
|
||||
@@ -109,7 +109,7 @@
|
||||
" [9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9],\n",
|
||||
" [7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0],\n",
|
||||
" ]\n",
|
||||
" data['time_windows'] = [\n",
|
||||
" data[\"time_windows\"] = [\n",
|
||||
" (0, 5), # depot\n",
|
||||
" (7, 12), # 1\n",
|
||||
" (10, 15), # 2\n",
|
||||
@@ -128,34 +128,36 @@
|
||||
" (10, 15), # 15\n",
|
||||
" (11, 15), # 16\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(data, manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {solution.ObjectiveValue()}')\n",
|
||||
" time_dimension = routing.GetDimensionOrDie('Time')\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(\"Time\")\n",
|
||||
" total_time = 0\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",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" time_var = time_dimension.CumulVar(index)\n",
|
||||
" plan_output += (\n",
|
||||
" f'{manager.IndexToNode(index)}'\n",
|
||||
" f' Time({solution.Min(time_var)},{solution.Max(time_var)})'\n",
|
||||
" ' -> ')\n",
|
||||
" f\"{manager.IndexToNode(index)}\"\n",
|
||||
" f\" Time({solution.Min(time_var)},{solution.Max(time_var)})\"\n",
|
||||
" \" -> \"\n",
|
||||
" )\n",
|
||||
" index = solution.Value(routing.NextVar(index))\n",
|
||||
" time_var = time_dimension.CumulVar(index)\n",
|
||||
" plan_output += (\n",
|
||||
" f'{manager.IndexToNode(index)}'\n",
|
||||
" f' Time({solution.Min(time_var)},{solution.Max(time_var)})\\n')\n",
|
||||
" plan_output += f'Time of the route: {solution.Min(time_var)}min\\n'\n",
|
||||
" f\"{manager.IndexToNode(index)}\"\n",
|
||||
" f\" Time({solution.Min(time_var)},{solution.Max(time_var)})\\n\"\n",
|
||||
" )\n",
|
||||
" plan_output += f\"Time of the route: {solution.Min(time_var)}min\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_time += solution.Min(time_var)\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",
|
||||
@@ -164,20 +166,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",
|
||||
@@ -185,39 +187,41 @@
|
||||
" 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",
|
||||
" 30, # allow waiting time\n",
|
||||
" 30, # 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",
|
||||
" # Add time window constraints for each location except depot.\n",
|
||||
" for location_idx, time_window in enumerate(data['time_windows']):\n",
|
||||
" if location_idx == data['depot']:\n",
|
||||
" for location_idx, time_window in enumerate(data[\"time_windows\"]):\n",
|
||||
" if location_idx == data[\"depot\"]:\n",
|
||||
" continue\n",
|
||||
" index = manager.NodeToIndex(location_idx)\n",
|
||||
" time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])\n",
|
||||
" # Add time window constraints for each vehicle start node.\n",
|
||||
" 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(\n",
|
||||
" data['time_windows'][depot_idx][0],\n",
|
||||
" data['time_windows'][depot_idx][1])\n",
|
||||
" data[\"time_windows\"][depot_idx][0], data[\"time_windows\"][depot_idx][1]\n",
|
||||
" )\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",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Simple VRP with special locations which need to be visited at end of the route.\n"
|
||||
]
|
||||
},
|
||||
@@ -90,7 +91,7 @@
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" # Special location don't consume token, while regular one consume one\n",
|
||||
" data['tokens'] = [\n",
|
||||
" data[\"tokens\"] = [\n",
|
||||
" 0, # 0 depot\n",
|
||||
" 0, # 1 special node\n",
|
||||
" 0, # 2 special node\n",
|
||||
@@ -112,20 +113,20 @@
|
||||
" -1, # 18\n",
|
||||
" ]\n",
|
||||
" # just need to be big enough, not a limiting factor\n",
|
||||
" data['vehicle_tokens'] = [20, 20, 20, 20]\n",
|
||||
" data['num_vehicles'] = 4\n",
|
||||
" data['depot'] = 0\n",
|
||||
" data[\"vehicle_tokens\"] = [20, 20, 20, 20]\n",
|
||||
" data[\"num_vehicles\"] = 4\n",
|
||||
" data[\"depot\"] = 0\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(manager, routing, solution):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {solution.ObjectiveValue()}')\n",
|
||||
" token_dimension = routing.GetDimensionOrDie('Token')\n",
|
||||
" print(f\"Objective: {solution.ObjectiveValue()}\")\n",
|
||||
" token_dimension = routing.GetDimensionOrDie(\"Token\")\n",
|
||||
" total_distance = 0\n",
|
||||
" total_token = 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",
|
||||
" total_token += solution.Value(token_dimension.CumulVar(index))\n",
|
||||
" route_distance = 0\n",
|
||||
@@ -134,20 +135,21 @@
|
||||
" node_index = manager.IndexToNode(index)\n",
|
||||
" token_var = token_dimension.CumulVar(index)\n",
|
||||
" route_token = solution.Value(token_var)\n",
|
||||
" plan_output += f' {node_index} Token({route_token}) -> '\n",
|
||||
" plan_output += f\" {node_index} Token({route_token}) -> \"\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_id\n",
|
||||
" )\n",
|
||||
" node_index = manager.IndexToNode(index)\n",
|
||||
" token_var = token_dimension.CumulVar(index)\n",
|
||||
" route_token = solution.Value(token_var)\n",
|
||||
" plan_output += f' {node_index} Token({route_token})\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" plan_output += f\" {node_index} Token({route_token})\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" total_distance += route_distance\n",
|
||||
" print(plan_output)\n",
|
||||
" print(f'Total distance of all routes: {total_distance}m')\n",
|
||||
" print(f'Total token of all routes: {total_token}')\n",
|
||||
" print(f\"Total distance of all routes: {total_distance}m\")\n",
|
||||
" print(f\"Total token of all routes: {total_token}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -156,8 +158,9 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the routing index manager.\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(len(data['tokens']),\n",
|
||||
" data['num_vehicles'], data['depot'])\n",
|
||||
" manager = pywrapcp.RoutingIndexManager(\n",
|
||||
" len(data[\"tokens\"]), data[\"num_vehicles\"], data[\"depot\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create Routing Model.\n",
|
||||
" routing = pywrapcp.RoutingModel(manager)\n",
|
||||
@@ -176,8 +179,9 @@
|
||||
" 0, # null slack\n",
|
||||
" 3000, # maximum distance per vehicle\n",
|
||||
" True, # start cumul to zero\n",
|
||||
" 'distance')\n",
|
||||
" distance_dimension = routing.GetDimensionOrDie('distance')\n",
|
||||
" \"distance\",\n",
|
||||
" )\n",
|
||||
" distance_dimension = routing.GetDimensionOrDie(\"distance\")\n",
|
||||
" distance_dimension.SetGlobalSpanCostCoefficient(100)\n",
|
||||
"\n",
|
||||
" # Define cost of each arc.\n",
|
||||
@@ -188,17 +192,18 @@
|
||||
" \"\"\"Returns the number of token consumed by the node.\"\"\"\n",
|
||||
" # Convert from routing variable Index to tokens NodeIndex.\n",
|
||||
" from_node = manager.IndexToNode(from_index)\n",
|
||||
" return data['tokens'][from_node]\n",
|
||||
" return data[\"tokens\"][from_node]\n",
|
||||
"\n",
|
||||
" token_callback_index = routing.RegisterUnaryTransitCallback(token_callback)\n",
|
||||
" routing.AddDimensionWithVehicleCapacity(\n",
|
||||
" token_callback_index,\n",
|
||||
" 0, # null capacity slack\n",
|
||||
" data['vehicle_tokens'], # vehicle maximum tokens\n",
|
||||
" data[\"vehicle_tokens\"], # vehicle maximum tokens\n",
|
||||
" False, # start cumul to zero\n",
|
||||
" 'Token')\n",
|
||||
" \"Token\",\n",
|
||||
" )\n",
|
||||
" # Add constraint: special node can only be visited if token remaining is zero\n",
|
||||
" token_dimension = routing.GetDimensionOrDie('Token')\n",
|
||||
" token_dimension = routing.GetDimensionOrDie(\"Token\")\n",
|
||||
" for node in range(1, 6):\n",
|
||||
" index = manager.NodeToIndex(node)\n",
|
||||
" routing.solver().Add(token_dimension.CumulVar(index) == 0)\n",
|
||||
@@ -206,16 +211,20 @@
|
||||
" # Instantiate route start and end times to produce feasible times.\n",
|
||||
" for i in range(manager.GetNumberOfVehicles()):\n",
|
||||
" routing.AddVariableMinimizedByFinalizer(\n",
|
||||
" token_dimension.CumulVar(routing.Start(i)))\n",
|
||||
" token_dimension.CumulVar(routing.Start(i))\n",
|
||||
" )\n",
|
||||
" routing.AddVariableMinimizedByFinalizer(\n",
|
||||
" token_dimension.CumulVar(routing.End(i)))\n",
|
||||
" token_dimension.CumulVar(routing.End(i))\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",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.time_limit.FromSeconds(1)\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
@@ -225,7 +234,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",
|
||||
|
||||
@@ -89,23 +89,24 @@
|
||||
"\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",
|
||||
" max_route_distance = 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",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" plan_output += f' {manager.IndexToNode(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",
|
||||
" plan_output += f'{manager.IndexToNode(index)}\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" )\n",
|
||||
" plan_output += f\"{manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" max_route_distance = max(route_distance, max_route_distance)\n",
|
||||
" print(f'Maximum of the route distances: {max_route_distance}m')\n",
|
||||
" print(f\"Maximum of the route distances: {max_route_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -134,22 +135,25 @@
|
||||
" 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",
|
||||
" 3000, # 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",
|
||||
" distance_dimension.SetGlobalSpanCostCoefficient(100)\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",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
" search_parameters.local_search_metaheuristic = (\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)\n",
|
||||
" routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH\n",
|
||||
" )\n",
|
||||
" search_parameters.log_search = True\n",
|
||||
" search_parameters.time_limit.FromSeconds(5)\n",
|
||||
"\n",
|
||||
|
||||
@@ -101,55 +101,50 @@
|
||||
" data = {}\n",
|
||||
" # Locations in block unit\n",
|
||||
" locations_ = [\n",
|
||||
" (4, 4), # depot\n",
|
||||
" (2, 0),\n",
|
||||
" (8, 0), # locations to visit\n",
|
||||
" (0, 1),\n",
|
||||
" (1, 1),\n",
|
||||
" (5, 2),\n",
|
||||
" (7, 2),\n",
|
||||
" (3, 3),\n",
|
||||
" (6, 3),\n",
|
||||
" (5, 5),\n",
|
||||
" (8, 5),\n",
|
||||
" (1, 6),\n",
|
||||
" (2, 6),\n",
|
||||
" (3, 7),\n",
|
||||
" (6, 7),\n",
|
||||
" (0, 8),\n",
|
||||
" (7, 8),\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",
|
||||
" # 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['num_vehicles'] = 4\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[\"num_vehicles\"] = 4\n",
|
||||
" data[\"depot\"] = 0\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_solution(data, manager, routing, assignment):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" print(f'Objective: {assignment.ObjectiveValue()}')\n",
|
||||
" print(f\"Objective: {assignment.ObjectiveValue()}\")\n",
|
||||
" total_distance = 0\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",
|
||||
" route_distance = 0\n",
|
||||
" while not routing.IsEnd(index):\n",
|
||||
" plan_output += f' {manager.IndexToNode(index)} ->'\n",
|
||||
" plan_output += f\" {manager.IndexToNode(index)} ->\"\n",
|
||||
" previous_index = index\n",
|
||||
" index = assignment.Value(routing.NextVar(index))\n",
|
||||
" route_distance += routing.GetArcCostForVehicle(\n",
|
||||
" previous_index, index, vehicle_id)\n",
|
||||
" plan_output += f' {manager.IndexToNode(index)}\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" previous_index, index, vehicle_id\n",
|
||||
" )\n",
|
||||
" plan_output += f\" {manager.IndexToNode(index)}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += route_distance\n",
|
||||
" print(f'Total Distance of all routes: {total_distance}m')\n",
|
||||
" print(f\"Total Distance of all routes: {total_distance}m\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -158,22 +153,22 @@
|
||||
"#######################\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",
|
||||
" 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_index, to_index):\n",
|
||||
" \"\"\"Returns the manhattan distance between the two nodes.\"\"\"\n",
|
||||
@@ -187,13 +182,14 @@
|
||||
"\n",
|
||||
"def add_distance_dimension(routing, distance_evaluator_index):\n",
|
||||
" \"\"\"Add Global Span constraint.\"\"\"\n",
|
||||
" distance = 'Distance'\n",
|
||||
" distance = \"Distance\"\n",
|
||||
" routing.AddDimension(\n",
|
||||
" distance_evaluator_index,\n",
|
||||
" 0, # null slack\n",
|
||||
" 3000, # 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",
|
||||
@@ -206,15 +202,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",
|
||||
" functools.partial(create_distance_evaluator(data), manager))\n",
|
||||
" functools.partial(create_distance_evaluator(data), manager)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Define cost of each arc.\n",
|
||||
" routing.SetArcCostEvaluatorOfAllVehicles(distance_evaluator_index)\n",
|
||||
@@ -225,7 +223,8 @@
|
||||
" # Setting first solution heuristic.\n",
|
||||
" search_parameters = pywrapcp.DefaultRoutingSearchParameters()\n",
|
||||
" search_parameters.first_solution_strategy = (\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)\n",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
@@ -234,7 +233,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",
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
"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",
|
||||
@@ -109,7 +109,7 @@
|
||||
" [9, 10, 18, 6, 8, 12, 15, 8, 13, 9, 13, 3, 4, 5, 9, 0, 9],\n",
|
||||
" [7, 14, 9, 16, 14, 8, 5, 10, 6, 5, 4, 10, 8, 6, 2, 9, 0],\n",
|
||||
" ]\n",
|
||||
" data['time_windows'] = [\n",
|
||||
" data[\"time_windows\"] = [\n",
|
||||
" (0, 5), # depot\n",
|
||||
" (7, 12), # 1\n",
|
||||
" (10, 15), # 2\n",
|
||||
@@ -128,8 +128,8 @@
|
||||
" (10, 15), # 15\n",
|
||||
" (11, 15), # 16\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",
|
||||
@@ -137,21 +137,35 @@
|
||||
"def print_solution(routes, cumul_data):\n",
|
||||
" \"\"\"Print the solution.\"\"\"\n",
|
||||
" total_time = 0\n",
|
||||
" route_str = ''\n",
|
||||
" route_str = \"\"\n",
|
||||
" for i, route in enumerate(routes):\n",
|
||||
" route_str += 'Route ' + str(i) + ':\\n'\n",
|
||||
" route_str += \"Route \" + str(i) + \":\\n\"\n",
|
||||
" start_time = cumul_data[i][0][0]\n",
|
||||
" end_time = cumul_data[i][0][1]\n",
|
||||
" route_str += ' ' + str(route[0]) + \\\n",
|
||||
" ' Time(' + str(start_time) + ', ' + str(end_time) + ')'\n",
|
||||
" route_str += (\n",
|
||||
" \" \"\n",
|
||||
" + str(route[0])\n",
|
||||
" + \" Time(\"\n",
|
||||
" + str(start_time)\n",
|
||||
" + \", \"\n",
|
||||
" + str(end_time)\n",
|
||||
" + \")\"\n",
|
||||
" )\n",
|
||||
" for j in range(1, len(route)):\n",
|
||||
" start_time = cumul_data[i][j][0]\n",
|
||||
" end_time = cumul_data[i][j][1]\n",
|
||||
" route_str += ' -> ' + str(route[j]) + \\\n",
|
||||
" ' Time(' + str(start_time) + ', ' + str(end_time) + ')'\n",
|
||||
" route_str += f'\\n Route time: {start_time}min\\n\\n'\n",
|
||||
" route_str += (\n",
|
||||
" \" -> \"\n",
|
||||
" + str(route[j])\n",
|
||||
" + \" Time(\"\n",
|
||||
" + str(start_time)\n",
|
||||
" + \", \"\n",
|
||||
" + str(end_time)\n",
|
||||
" + \")\"\n",
|
||||
" )\n",
|
||||
" route_str += f\"\\n Route time: {start_time}min\\n\\n\"\n",
|
||||
" total_time += cumul_data[i][len(route) - 1][0]\n",
|
||||
" route_str += f'Total time: {total_time}min'\n",
|
||||
" route_str += f\"Total time: {total_time}min\"\n",
|
||||
" print(route_str)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -200,20 +214,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",
|
||||
@@ -221,38 +235,41 @@
|
||||
" routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)\n",
|
||||
"\n",
|
||||
" # Add Time Windows constraint.\n",
|
||||
" time = 'Time'\n",
|
||||
" time = \"Time\"\n",
|
||||
"\n",
|
||||
" routing.AddDimension(\n",
|
||||
" transit_callback_index,\n",
|
||||
" 30, # allow waiting time\n",
|
||||
" 30, # maximum time per vehicle\n",
|
||||
" False, # Don't force cumulative time to be 0 at start of routes.\n",
|
||||
" time)\n",
|
||||
" time,\n",
|
||||
" )\n",
|
||||
" time_dimension = routing.GetDimensionOrDie(time)\n",
|
||||
" # Add time window constraints for each location except depot.\n",
|
||||
" for location_idx, time_window in enumerate(data['time_windows']):\n",
|
||||
" for location_idx, time_window in enumerate(data[\"time_windows\"]):\n",
|
||||
" if location_idx == 0:\n",
|
||||
" continue\n",
|
||||
" index = manager.NodeToIndex(location_idx)\n",
|
||||
" time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])\n",
|
||||
" # Add time window constraints for each vehicle start node.\n",
|
||||
" for vehicle_id in range(data['num_vehicles']):\n",
|
||||
" 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",
|
||||
"\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",
|
||||
" routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve the problem.\n",
|
||||
" solution = routing.SolveWithParameters(search_parameters)\n",
|
||||
|
||||
@@ -92,12 +92,11 @@
|
||||
"from ortools.linear_solver import pywraplp\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"_LOAD_MIN = flags.DEFINE_integer('load_min', 480, 'Minimum load in minutes.')\n",
|
||||
"_LOAD_MAX = flags.DEFINE_integer('load_max', 540, 'Maximum load in minutes.')\n",
|
||||
"_COMMUTE_TIME = flags.DEFINE_integer('commute_time', 30,\n",
|
||||
" 'Commute time in minutes.')\n",
|
||||
"_NUM_WORKERS = flags.DEFINE_integer('num_workers', 98,\n",
|
||||
" 'Maximum number of workers.')\n",
|
||||
"\n",
|
||||
"_LOAD_MIN = flags.DEFINE_integer(\"load_min\", 480, \"Minimum load in minutes.\")\n",
|
||||
"_LOAD_MAX = flags.DEFINE_integer(\"load_max\", 540, \"Maximum load in minutes.\")\n",
|
||||
"_COMMUTE_TIME = flags.DEFINE_integer(\"commute_time\", 30, \"Commute time in minutes.\")\n",
|
||||
"_NUM_WORKERS = flags.DEFINE_integer(\"num_workers\", 98, \"Maximum number of workers.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class AllSolutionCollector(cp_model.CpSolverSolutionCallback):\n",
|
||||
@@ -118,24 +117,21 @@
|
||||
" return self.__collect\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def EnumerateAllKnapsacksWithRepetition(item_sizes, total_size_min,\n",
|
||||
" total_size_max):\n",
|
||||
"def EnumerateAllKnapsacksWithRepetition(item_sizes, total_size_min, total_size_max):\n",
|
||||
" \"\"\"Enumerate all possible knapsacks with total size in the given range.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" item_sizes: a list of integers. item_sizes[i] is the size of item #i.\n",
|
||||
" total_size_min: an integer, the minimum total size.\n",
|
||||
" total_size_max: an integer, the maximum total size.\n",
|
||||
" Args:\n",
|
||||
" item_sizes: a list of integers. item_sizes[i] is the size of item #i.\n",
|
||||
" total_size_min: an integer, the minimum total size.\n",
|
||||
" total_size_max: an integer, the maximum total size.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" The list of all the knapsacks whose total size is in the given inclusive\n",
|
||||
" range. Each knapsack is a list [#item0, #item1, ... ], where #itemK is an\n",
|
||||
" nonnegative integer: the number of times we put item #K in the knapsack.\n",
|
||||
" \"\"\"\n",
|
||||
" Returns:\n",
|
||||
" The list of all the knapsacks whose total size is in the given inclusive\n",
|
||||
" range. Each knapsack is a list [#item0, #item1, ... ], where #itemK is an\n",
|
||||
" nonnegative integer: the number of times we put item #K in the knapsack.\n",
|
||||
" \"\"\"\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
" variables = [\n",
|
||||
" model.NewIntVar(0, total_size_max // size, '') for size in item_sizes\n",
|
||||
" ]\n",
|
||||
" variables = [model.NewIntVar(0, total_size_max // size, \"\") for size in item_sizes]\n",
|
||||
" load = sum(variables[i] * size for i, size in enumerate(item_sizes))\n",
|
||||
" model.AddLinearConstraint(load, total_size_min, total_size_max)\n",
|
||||
"\n",
|
||||
@@ -148,52 +144,52 @@
|
||||
" return solution_collector.combinations()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def AggregateItemCollectionsOptimally(item_collections, max_num_collections,\n",
|
||||
" ideal_item_ratios):\n",
|
||||
"def AggregateItemCollectionsOptimally(\n",
|
||||
" item_collections, max_num_collections, ideal_item_ratios\n",
|
||||
"):\n",
|
||||
" \"\"\"Selects a set (with repetition) of combination of items optimally.\n",
|
||||
"\n",
|
||||
" Given a set of collections of N possible items (in each collection, an item\n",
|
||||
" may appear multiple times), a given \"ideal breakdown of items\", and a\n",
|
||||
" maximum number of collections, this method finds the optimal way to\n",
|
||||
" aggregate the collections in order to:\n",
|
||||
" - maximize the overall number of items\n",
|
||||
" - while keeping the ratio of each item, among the overall selection, as close\n",
|
||||
" as possible to a given input ratio (which depends on the item).\n",
|
||||
" Each collection may be selected more than one time.\n",
|
||||
" Given a set of collections of N possible items (in each collection, an item\n",
|
||||
" may appear multiple times), a given \"ideal breakdown of items\", and a\n",
|
||||
" maximum number of collections, this method finds the optimal way to\n",
|
||||
" aggregate the collections in order to:\n",
|
||||
" - maximize the overall number of items\n",
|
||||
" - while keeping the ratio of each item, among the overall selection, as close\n",
|
||||
" as possible to a given input ratio (which depends on the item).\n",
|
||||
" Each collection may be selected more than one time.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" item_collections: a list of item collections. Each item collection is a list\n",
|
||||
" of integers [#item0, ..., #itemN-1], where #itemK is the number of times\n",
|
||||
" item #K appears in the collection, and N is the number of distinct items.\n",
|
||||
" max_num_collections: an integer, the maximum number of item collections that\n",
|
||||
" may be selected (counting repetitions of the same collection).\n",
|
||||
" ideal_item_ratios: A list of N float which sums to 1.0: the K-th element is\n",
|
||||
" the ideal ratio of item #K in the whole aggregated selection.\n",
|
||||
" Args:\n",
|
||||
" item_collections: a list of item collections. Each item collection is a list\n",
|
||||
" of integers [#item0, ..., #itemN-1], where #itemK is the number of times\n",
|
||||
" item #K appears in the collection, and N is the number of distinct items.\n",
|
||||
" max_num_collections: an integer, the maximum number of item collections that\n",
|
||||
" may be selected (counting repetitions of the same collection).\n",
|
||||
" ideal_item_ratios: A list of N float which sums to 1.0: the K-th element is\n",
|
||||
" the ideal ratio of item #K in the whole aggregated selection.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" A pair (objective value, list of pairs (item collection, num_selections)),\n",
|
||||
" where:\n",
|
||||
" - \"objective value\" is the value of the internal objective function used\n",
|
||||
" by the MIP Solver\n",
|
||||
" - Each \"item collection\" is an element of the input item_collections\n",
|
||||
" - and its associated \"num_selections\" is the number of times it was\n",
|
||||
" selected.\n",
|
||||
" \"\"\"\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('SCIP')\n",
|
||||
" Returns:\n",
|
||||
" A pair (objective value, list of pairs (item collection, num_selections)),\n",
|
||||
" where:\n",
|
||||
" - \"objective value\" is the value of the internal objective function used\n",
|
||||
" by the MIP Solver\n",
|
||||
" - Each \"item collection\" is an element of the input item_collections\n",
|
||||
" - and its associated \"num_selections\" is the number of times it was\n",
|
||||
" selected.\n",
|
||||
" \"\"\"\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"SCIP\")\n",
|
||||
" if not solver:\n",
|
||||
" return []\n",
|
||||
" n = len(ideal_item_ratios)\n",
|
||||
" num_distinct_collections = len(item_collections)\n",
|
||||
" max_num_items_per_collection = 0\n",
|
||||
" for template in item_collections:\n",
|
||||
" max_num_items_per_collection = max(max_num_items_per_collection,\n",
|
||||
" sum(template))\n",
|
||||
" max_num_items_per_collection = max(max_num_items_per_collection, sum(template))\n",
|
||||
" upper_bound = max_num_items_per_collection * max_num_collections\n",
|
||||
"\n",
|
||||
" # num_selections_of_collection[i] is an IntVar that represents the number\n",
|
||||
" # of times that we will use collection #i in our global selection.\n",
|
||||
" num_selections_of_collection = [\n",
|
||||
" solver.IntVar(0, max_num_collections, 's[%d]' % i)\n",
|
||||
" solver.IntVar(0, max_num_collections, \"s[%d]\" % i)\n",
|
||||
" for i in range(num_distinct_collections)\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
@@ -201,19 +197,17 @@
|
||||
" # aggregated over all selected collections. This is enforced with dedicated\n",
|
||||
" # constraints that bind them with the num_selections_of_collection vars.\n",
|
||||
" num_overall_item = [\n",
|
||||
" solver.IntVar(0, upper_bound, 'num_overall_item[%d]' % i)\n",
|
||||
" for i in range(n)\n",
|
||||
" solver.IntVar(0, upper_bound, \"num_overall_item[%d]\" % i) for i in range(n)\n",
|
||||
" ]\n",
|
||||
" for i in range(n):\n",
|
||||
" ct = solver.Constraint(0.0, 0.0)\n",
|
||||
" ct.SetCoefficient(num_overall_item[i], -1)\n",
|
||||
" for j in range(num_distinct_collections):\n",
|
||||
" ct.SetCoefficient(num_selections_of_collection[j],\n",
|
||||
" item_collections[j][i])\n",
|
||||
" ct.SetCoefficient(num_selections_of_collection[j], item_collections[j][i])\n",
|
||||
"\n",
|
||||
" # Maintain the num_all_item variable as the sum of all num_overall_item\n",
|
||||
" # variables.\n",
|
||||
" num_all_items = solver.IntVar(0, upper_bound, 'num_all_items')\n",
|
||||
" num_all_items = solver.IntVar(0, upper_bound, \"num_all_items\")\n",
|
||||
" solver.Add(solver.Sum(num_overall_item) == num_all_items)\n",
|
||||
"\n",
|
||||
" # Sets the total number of workers.\n",
|
||||
@@ -221,15 +215,16 @@
|
||||
"\n",
|
||||
" # Objective variables.\n",
|
||||
" deviation_vars = [\n",
|
||||
" solver.NumVar(0, upper_bound, 'deviation_vars[%d]' % i)\n",
|
||||
" for i in range(n)\n",
|
||||
" solver.NumVar(0, upper_bound, \"deviation_vars[%d]\" % i) for i in range(n)\n",
|
||||
" ]\n",
|
||||
" for i in range(n):\n",
|
||||
" deviation = deviation_vars[i]\n",
|
||||
" solver.Add(deviation >= num_overall_item[i] -\n",
|
||||
" ideal_item_ratios[i] * num_all_items)\n",
|
||||
" solver.Add(deviation >= ideal_item_ratios[i] * num_all_items -\n",
|
||||
" num_overall_item[i])\n",
|
||||
" solver.Add(\n",
|
||||
" deviation >= num_overall_item[i] - ideal_item_ratios[i] * num_all_items\n",
|
||||
" )\n",
|
||||
" solver.Add(\n",
|
||||
" deviation >= ideal_item_ratios[i] * num_all_items - num_overall_item[i]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" solver.Maximize(num_all_items - solver.Sum(deviation_vars))\n",
|
||||
"\n",
|
||||
@@ -244,43 +239,57 @@
|
||||
"def GetOptimalSchedule(demand):\n",
|
||||
" \"\"\"Computes the optimal schedule for the installation input.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" demand: a list of \"appointment types\". Each \"appointment type\" is a triple\n",
|
||||
" (ideal_ratio_pct, name, duration_minutes), where ideal_ratio_pct is the\n",
|
||||
" ideal percentage (in [0..100.0]) of that type of appointment among all\n",
|
||||
" appointments scheduled.\n",
|
||||
" Args:\n",
|
||||
" demand: a list of \"appointment types\". Each \"appointment type\" is a triple\n",
|
||||
" (ideal_ratio_pct, name, duration_minutes), where ideal_ratio_pct is the\n",
|
||||
" ideal percentage (in [0..100.0]) of that type of appointment among all\n",
|
||||
" appointments scheduled.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" The same output type as EnumerateAllKnapsacksWithRepetition.\n",
|
||||
" \"\"\"\n",
|
||||
" Returns:\n",
|
||||
" The same output type as EnumerateAllKnapsacksWithRepetition.\n",
|
||||
" \"\"\"\n",
|
||||
" combinations = EnumerateAllKnapsacksWithRepetition(\n",
|
||||
" [a[2] + _COMMUTE_TIME.value for a in demand], _LOAD_MIN.value,\n",
|
||||
" _LOAD_MAX.value)\n",
|
||||
" print(('Found %d possible day schedules ' % len(combinations) +\n",
|
||||
" '(i.e. combination of appointments filling up one worker\\'s day)'))\n",
|
||||
" [a[2] + _COMMUTE_TIME.value for a in demand], _LOAD_MIN.value, _LOAD_MAX.value\n",
|
||||
" )\n",
|
||||
" print(\n",
|
||||
" (\n",
|
||||
" \"Found %d possible day schedules \" % len(combinations)\n",
|
||||
" + \"(i.e. combination of appointments filling up one worker's day)\"\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" selection = AggregateItemCollectionsOptimally(\n",
|
||||
" combinations, _NUM_WORKERS.value, [a[0] / 100.0 for a in demand])\n",
|
||||
" combinations, _NUM_WORKERS.value, [a[0] / 100.0 for a in demand]\n",
|
||||
" )\n",
|
||||
" output = []\n",
|
||||
" for i in range(len(selection)):\n",
|
||||
" if selection[i] != 0:\n",
|
||||
" output.append((selection[i], [(combinations[i][t], demand[t][1])\n",
|
||||
" for t in range(len(demand))\n",
|
||||
" if combinations[i][t] != 0]))\n",
|
||||
" output.append(\n",
|
||||
" (\n",
|
||||
" selection[i],\n",
|
||||
" [\n",
|
||||
" (combinations[i][t], demand[t][1])\n",
|
||||
" for t in range(len(demand))\n",
|
||||
" if combinations[i][t] != 0\n",
|
||||
" ],\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" return output\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(_):\n",
|
||||
" demand = [(45.0, 'Type1', 90), (30.0, 'Type2', 120), (25.0, 'Type3', 180)]\n",
|
||||
" print('*** input problem ***')\n",
|
||||
" print('Appointments: ')\n",
|
||||
" demand = [(45.0, \"Type1\", 90), (30.0, \"Type2\", 120), (25.0, \"Type3\", 180)]\n",
|
||||
" print(\"*** input problem ***\")\n",
|
||||
" print(\"Appointments: \")\n",
|
||||
" for a in demand:\n",
|
||||
" print(' %.2f%% of %s : %d min' % (a[0], a[1], a[2]))\n",
|
||||
" print('Commute time = %d' % _COMMUTE_TIME.value)\n",
|
||||
" print('Acceptable duration of a work day = [%d..%d]' %\n",
|
||||
" (_LOAD_MIN.value, _LOAD_MAX.value))\n",
|
||||
" print('%d workers' % _NUM_WORKERS.value)\n",
|
||||
" print(\" %.2f%% of %s : %d min\" % (a[0], a[1], a[2]))\n",
|
||||
" print(\"Commute time = %d\" % _COMMUTE_TIME.value)\n",
|
||||
" print(\n",
|
||||
" \"Acceptable duration of a work day = [%d..%d]\"\n",
|
||||
" % (_LOAD_MIN.value, _LOAD_MAX.value)\n",
|
||||
" )\n",
|
||||
" print(\"%d workers\" % _NUM_WORKERS.value)\n",
|
||||
" selection = GetOptimalSchedule(demand)\n",
|
||||
" print()\n",
|
||||
" installed = 0\n",
|
||||
@@ -288,27 +297,27 @@
|
||||
" for a in demand:\n",
|
||||
" installed_per_type[a[1]] = 0\n",
|
||||
"\n",
|
||||
" print('*** output solution ***')\n",
|
||||
" print(\"*** output solution ***\")\n",
|
||||
" for template in selection:\n",
|
||||
" num_instances = template[0]\n",
|
||||
" print('%d schedules with ' % num_instances)\n",
|
||||
" print(\"%d schedules with \" % num_instances)\n",
|
||||
" for t in template[1]:\n",
|
||||
" mult = t[0]\n",
|
||||
" print(' %d installation of type %s' % (mult, t[1]))\n",
|
||||
" print(\" %d installation of type %s\" % (mult, t[1]))\n",
|
||||
" installed += num_instances * mult\n",
|
||||
" installed_per_type[t[1]] += num_instances * mult\n",
|
||||
"\n",
|
||||
" print()\n",
|
||||
" print('%d installations planned' % installed)\n",
|
||||
" print(\"%d installations planned\" % installed)\n",
|
||||
" for a in demand:\n",
|
||||
" name = a[1]\n",
|
||||
" per_type = installed_per_type[name]\n",
|
||||
" if installed != 0:\n",
|
||||
" print(\n",
|
||||
" f' {per_type} ({per_type * 100.0 / installed}%) installations of type {name} planned'\n",
|
||||
" f\" {per_type} ({per_type * 100.0 / installed}%) installations of type {name} planned\"\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print(f' {per_type} installations of type {name} planned')\n",
|
||||
" print(f\" {per_type} installations of type {name} planned\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solve an assignment problem with combination constraints on workers.\n"
|
||||
]
|
||||
},
|
||||
@@ -89,19 +90,27 @@
|
||||
"def solve_assignment():\n",
|
||||
" \"\"\"Solve the assignment problem.\"\"\"\n",
|
||||
" # Data.\n",
|
||||
" cost = [[90, 76, 75, 70, 50, 74], [35, 85, 55, 65, 48, 101],\n",
|
||||
" [125, 95, 90, 105, 59, 120], [45, 110, 95, 115, 104, 83],\n",
|
||||
" [60, 105, 80, 75, 59, 62], [45, 65, 110, 95, 47, 31],\n",
|
||||
" [38, 51, 107, 41, 69, 99], [47, 85, 57, 71, 92, 77],\n",
|
||||
" [39, 63, 97, 49, 118, 56], [47, 101, 71, 60, 88, 109],\n",
|
||||
" [17, 39, 103, 64, 61, 92], [101, 45, 83, 59, 92, 27]]\n",
|
||||
" cost = [\n",
|
||||
" [90, 76, 75, 70, 50, 74],\n",
|
||||
" [35, 85, 55, 65, 48, 101],\n",
|
||||
" [125, 95, 90, 105, 59, 120],\n",
|
||||
" [45, 110, 95, 115, 104, 83],\n",
|
||||
" [60, 105, 80, 75, 59, 62],\n",
|
||||
" [45, 65, 110, 95, 47, 31],\n",
|
||||
" [38, 51, 107, 41, 69, 99],\n",
|
||||
" [47, 85, 57, 71, 92, 77],\n",
|
||||
" [39, 63, 97, 49, 118, 56],\n",
|
||||
" [47, 101, 71, 60, 88, 109],\n",
|
||||
" [17, 39, 103, 64, 61, 92],\n",
|
||||
" [101, 45, 83, 59, 92, 27],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" group1 = [\n",
|
||||
" [0, 0, 1, 1], # Workers 2, 3\n",
|
||||
" [0, 1, 0, 1], # Workers 1, 3\n",
|
||||
" [0, 1, 1, 0], # Workers 1, 2\n",
|
||||
" [1, 1, 0, 0], # Workers 0, 1\n",
|
||||
" [1, 0, 1, 0]\n",
|
||||
" [1, 0, 1, 0],\n",
|
||||
" ] # Workers 0, 2\n",
|
||||
"\n",
|
||||
" group2 = [\n",
|
||||
@@ -109,7 +118,7 @@
|
||||
" [0, 1, 0, 1], # Workers 5, 7\n",
|
||||
" [0, 1, 1, 0], # Workers 5, 6\n",
|
||||
" [1, 1, 0, 0], # Workers 4, 5\n",
|
||||
" [1, 0, 0, 1]\n",
|
||||
" [1, 0, 0, 1],\n",
|
||||
" ] # Workers 4, 7\n",
|
||||
"\n",
|
||||
" group3 = [\n",
|
||||
@@ -117,7 +126,7 @@
|
||||
" [0, 1, 0, 1], # Workers 9, 11\n",
|
||||
" [0, 1, 1, 0], # Workers 9, 10\n",
|
||||
" [1, 0, 1, 0], # Workers 8, 10\n",
|
||||
" [1, 0, 0, 1]\n",
|
||||
" [1, 0, 0, 1],\n",
|
||||
" ] # Workers 8, 11\n",
|
||||
"\n",
|
||||
" sizes = [10, 7, 3, 12, 15, 4, 11, 5]\n",
|
||||
@@ -131,10 +140,10 @@
|
||||
"\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
" # Variables\n",
|
||||
" selected = [[model.NewBoolVar('x[%i,%i]' % (i, j))\n",
|
||||
" for j in all_tasks]\n",
|
||||
" for i in all_workers]\n",
|
||||
" works = [model.NewBoolVar('works[%i]' % i) for i in all_workers]\n",
|
||||
" selected = [\n",
|
||||
" [model.NewBoolVar(\"x[%i,%i]\" % (i, j)) for j in all_tasks] for i in all_workers\n",
|
||||
" ]\n",
|
||||
" works = [model.NewBoolVar(\"works[%i]\" % i) for i in all_workers]\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
"\n",
|
||||
@@ -148,47 +157,43 @@
|
||||
"\n",
|
||||
" # Total task size for each worker is at most total_size_max\n",
|
||||
" for i in all_workers:\n",
|
||||
" model.Add(\n",
|
||||
" sum(sizes[j] * selected[i][j] for j in all_tasks) <= total_size_max)\n",
|
||||
" model.Add(sum(sizes[j] * selected[i][j] for j in all_tasks) <= total_size_max)\n",
|
||||
"\n",
|
||||
" # Group constraints.\n",
|
||||
" model.AddAllowedAssignments([works[0], works[1], works[2], works[3]],\n",
|
||||
" group1)\n",
|
||||
" model.AddAllowedAssignments([works[4], works[5], works[6], works[7]],\n",
|
||||
" group2)\n",
|
||||
" model.AddAllowedAssignments([works[8], works[9], works[10], works[11]],\n",
|
||||
" group3)\n",
|
||||
" model.AddAllowedAssignments([works[0], works[1], works[2], works[3]], group1)\n",
|
||||
" model.AddAllowedAssignments([works[4], works[5], works[6], works[7]], group2)\n",
|
||||
" model.AddAllowedAssignments([works[8], works[9], works[10], works[11]], group3)\n",
|
||||
"\n",
|
||||
" # Objective\n",
|
||||
" model.Minimize(\n",
|
||||
" sum(selected[i][j] * cost[i][j]\n",
|
||||
" for j in all_tasks\n",
|
||||
" for i in all_workers))\n",
|
||||
" sum(selected[i][j] * cost[i][j] for j in all_tasks for i in all_workers)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solve and output solution.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" status = solver.Solve(model)\n",
|
||||
"\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" print('Total cost = %i' % solver.ObjectiveValue())\n",
|
||||
" print(\"Total cost = %i\" % solver.ObjectiveValue())\n",
|
||||
" print()\n",
|
||||
" for i in all_workers:\n",
|
||||
" for j in all_tasks:\n",
|
||||
" if solver.BooleanValue(selected[i][j]):\n",
|
||||
" print('Worker ', i, ' assigned to task ', j, ' Cost = ',\n",
|
||||
" cost[i][j])\n",
|
||||
" print(\n",
|
||||
" \"Worker \", i, \" assigned to task \", j, \" Cost = \", cost[i][j]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" print()\n",
|
||||
"\n",
|
||||
" print('Statistics')\n",
|
||||
" print(' - conflicts : %i' % solver.NumConflicts())\n",
|
||||
" print(' - branches : %i' % solver.NumBranches())\n",
|
||||
" print(' - wall time : %f s' % solver.WallTime())\n",
|
||||
" print(\"Statistics\")\n",
|
||||
" print(\" - conflicts : %i\" % solver.NumConflicts())\n",
|
||||
" print(\" - branches : %i\" % solver.NumBranches())\n",
|
||||
" print(\" - wall time : %f s\" % solver.WallTime())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" solve_assignment()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"We are trying to group items in equal sized groups.\n",
|
||||
"\n",
|
||||
"Each item has a color and a value. We want the sum of values of each group to\n",
|
||||
@@ -105,10 +106,10 @@
|
||||
" self.__item_in_group = item_in_group\n",
|
||||
"\n",
|
||||
" def on_solution_callback(self):\n",
|
||||
" print('Solution %i' % self.__solution_count)\n",
|
||||
" print(\"Solution %i\" % self.__solution_count)\n",
|
||||
" self.__solution_count += 1\n",
|
||||
"\n",
|
||||
" print(' objective value = %i' % self.ObjectiveValue())\n",
|
||||
" print(\" objective value = %i\" % self.ObjectiveValue())\n",
|
||||
" groups = {}\n",
|
||||
" sums = {}\n",
|
||||
" for g in self.__all_groups:\n",
|
||||
@@ -121,19 +122,19 @@
|
||||
"\n",
|
||||
" for g in self.__all_groups:\n",
|
||||
" group = groups[g]\n",
|
||||
" print('group %i: sum = %0.2f [' % (g, sums[g]), end='')\n",
|
||||
" print(\"group %i: sum = %0.2f [\" % (g, sums[g]), end=\"\")\n",
|
||||
" for item in group:\n",
|
||||
" value = self.__values[item]\n",
|
||||
" color = self.__colors[item]\n",
|
||||
" print(' (%i, %i, %i)' % (item, value, color), end='')\n",
|
||||
" print(']')\n",
|
||||
" print(\" (%i, %i, %i)\" % (item, value, color), end=\"\")\n",
|
||||
" print(\"]\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" \"\"\"Solves a group balancing problem.\"\"\"\n",
|
||||
"\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" # Data.\n",
|
||||
" num_groups = 10\n",
|
||||
" num_items = 100\n",
|
||||
@@ -162,9 +163,11 @@
|
||||
" if colors[i] == c:\n",
|
||||
" items_per_color[c].append(i)\n",
|
||||
"\n",
|
||||
" print('Model has %i items, %i groups, and %i colors' %\n",
|
||||
" (num_items, num_groups, num_colors))\n",
|
||||
" print(' average sum per group = %i' % average_sum_per_group)\n",
|
||||
" print(\n",
|
||||
" \"Model has %i items, %i groups, and %i colors\"\n",
|
||||
" % (num_items, num_groups, num_colors)\n",
|
||||
" )\n",
|
||||
" print(\" average sum per group = %i\" % average_sum_per_group)\n",
|
||||
"\n",
|
||||
" # Model.\n",
|
||||
"\n",
|
||||
@@ -173,43 +176,42 @@
|
||||
" item_in_group = {}\n",
|
||||
" for i in all_items:\n",
|
||||
" for g in all_groups:\n",
|
||||
" item_in_group[(i, g)] = model.NewBoolVar('item %d in group %d' %\n",
|
||||
" (i, g))\n",
|
||||
" item_in_group[(i, g)] = model.NewBoolVar(\"item %d in group %d\" % (i, g))\n",
|
||||
"\n",
|
||||
" # Each group must have the same size.\n",
|
||||
" for g in all_groups:\n",
|
||||
" model.Add(\n",
|
||||
" sum(item_in_group[(i, g)]\n",
|
||||
" for i in all_items) == num_items_per_group)\n",
|
||||
" model.Add(sum(item_in_group[(i, g)] for i in all_items) == num_items_per_group)\n",
|
||||
"\n",
|
||||
" # One item must belong to exactly one group.\n",
|
||||
" for i in all_items:\n",
|
||||
" model.Add(sum(item_in_group[(i, g)] for g in all_groups) == 1)\n",
|
||||
"\n",
|
||||
" # The deviation of the sum of each items in a group against the average.\n",
|
||||
" e = model.NewIntVar(0, 550, 'epsilon')\n",
|
||||
" e = model.NewIntVar(0, 550, \"epsilon\")\n",
|
||||
"\n",
|
||||
" # Constrain the sum of values in one group around the average sum per group.\n",
|
||||
" for g in all_groups:\n",
|
||||
" model.Add(\n",
|
||||
" sum(item_in_group[(i, g)] * values[i]\n",
|
||||
" for i in all_items) <= average_sum_per_group + e)\n",
|
||||
" sum(item_in_group[(i, g)] * values[i] for i in all_items)\n",
|
||||
" <= average_sum_per_group + e\n",
|
||||
" )\n",
|
||||
" model.Add(\n",
|
||||
" sum(item_in_group[(i, g)] * values[i]\n",
|
||||
" for i in all_items) >= average_sum_per_group - e)\n",
|
||||
" sum(item_in_group[(i, g)] * values[i] for i in all_items)\n",
|
||||
" >= average_sum_per_group - e\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # color_in_group variables.\n",
|
||||
" color_in_group = {}\n",
|
||||
" for g in all_groups:\n",
|
||||
" for c in all_colors:\n",
|
||||
" color_in_group[(c, g)] = model.NewBoolVar(\n",
|
||||
" 'color %d is in group %d' % (c, g))\n",
|
||||
" \"color %d is in group %d\" % (c, g)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Item is in a group implies its color is in that group.\n",
|
||||
" for i in all_items:\n",
|
||||
" for g in all_groups:\n",
|
||||
" model.AddImplication(item_in_group[(i, g)],\n",
|
||||
" color_in_group[(colors[i], g)])\n",
|
||||
" model.AddImplication(item_in_group[(i, g)], color_in_group[(colors[i], g)])\n",
|
||||
"\n",
|
||||
" # If a color is in a group, it must contains at least\n",
|
||||
" # min_items_of_same_color_per_group items from that color.\n",
|
||||
@@ -217,8 +219,9 @@
|
||||
" for g in all_groups:\n",
|
||||
" literal = color_in_group[(c, g)]\n",
|
||||
" model.Add(\n",
|
||||
" sum(item_in_group[(i, g)] for i in items_per_color[c]) >=\n",
|
||||
" min_items_of_same_color_per_group).OnlyEnforceIf(literal)\n",
|
||||
" sum(item_in_group[(i, g)] for i in items_per_color[c])\n",
|
||||
" >= min_items_of_same_color_per_group\n",
|
||||
" ).OnlyEnforceIf(literal)\n",
|
||||
"\n",
|
||||
" # Compute the maximum number of colors in a group.\n",
|
||||
" max_color = num_items_per_group // min_items_of_same_color_per_group\n",
|
||||
@@ -226,8 +229,7 @@
|
||||
" # Redundant constraint, it helps with solving time.\n",
|
||||
" if max_color < num_colors:\n",
|
||||
" for g in all_groups:\n",
|
||||
" model.Add(\n",
|
||||
" sum(color_in_group[(c, g)] for c in all_colors) <= max_color)\n",
|
||||
" model.Add(sum(color_in_group[(c, g)] for c in all_colors) <= max_color)\n",
|
||||
"\n",
|
||||
" # Minimize epsilon\n",
|
||||
" model.Minimize(e)\n",
|
||||
@@ -235,18 +237,19 @@
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" # solver.parameters.log_search_progress = True\n",
|
||||
" solver.parameters.num_workers = 16\n",
|
||||
" solution_printer = SolutionPrinter(values, colors, all_groups, all_items,\n",
|
||||
" item_in_group)\n",
|
||||
" solution_printer = SolutionPrinter(\n",
|
||||
" values, colors, all_groups, all_items, item_in_group\n",
|
||||
" )\n",
|
||||
" status = solver.Solve(model, solution_printer)\n",
|
||||
"\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" print('Optimal epsilon: %i' % solver.ObjectiveValue())\n",
|
||||
" print('Statistics')\n",
|
||||
" print(' - conflicts : %i' % solver.NumConflicts())\n",
|
||||
" print(' - branches : %i' % solver.NumBranches())\n",
|
||||
" print(' - wall time : %f s' % solver.WallTime())\n",
|
||||
" print(\"Optimal epsilon: %i\" % solver.ObjectiveValue())\n",
|
||||
" print(\"Statistics\")\n",
|
||||
" print(\" - conflicts : %i\" % solver.NumConflicts())\n",
|
||||
" print(\" - branches : %i\" % solver.NumBranches())\n",
|
||||
" print(\" - wall time : %f s\" % solver.WallTime())\n",
|
||||
" else:\n",
|
||||
" print('No solution found')\n",
|
||||
" print(\"No solution found\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"We are trying to group items in equal sized groups.\n",
|
||||
"\n",
|
||||
"Each item has a color and a value. We want the sum of values of each group to be\n",
|
||||
@@ -127,9 +128,13 @@
|
||||
" max_set = [\n",
|
||||
" int(\n",
|
||||
" math.ceil(\n",
|
||||
" min(max_quantities[q][1] * 1000 / chemical_set[s][q + 1]\n",
|
||||
" min(\n",
|
||||
" max_quantities[q][1] * 1000 / chemical_set[s][q + 1]\n",
|
||||
" for q in all_products\n",
|
||||
" if chemical_set[s][q + 1] != 0)))\n",
|
||||
" if chemical_set[s][q + 1] != 0\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
" for s in all_sets\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
@@ -139,14 +144,13 @@
|
||||
"\n",
|
||||
" for p in all_products:\n",
|
||||
" model.Add(\n",
|
||||
" sum(\n",
|
||||
" int(chemical_set[s][p + 1] * 10) * set_vars[s]\n",
|
||||
" for s in all_sets) <= int(max_quantities[p][1] * 10000))\n",
|
||||
" sum(int(chemical_set[s][p + 1] * 10) * set_vars[s] for s in all_sets)\n",
|
||||
" <= int(max_quantities[p][1] * 10000)\n",
|
||||
" )\n",
|
||||
" model.Add(\n",
|
||||
" sum(\n",
|
||||
" int(chemical_set[s][p + 1] * 10) * set_vars[s]\n",
|
||||
" for s in all_sets) >= int(max_quantities[p][1] * 10000) -\n",
|
||||
" epsilon)\n",
|
||||
" sum(int(chemical_set[s][p + 1] * 10) * set_vars[s] for s in all_sets)\n",
|
||||
" >= int(max_quantities[p][1] * 10000) - epsilon\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" model.Minimize(epsilon)\n",
|
||||
"\n",
|
||||
@@ -158,15 +162,15 @@
|
||||
" print(f\"Optimal objective value = {solver.ObjectiveValue() / 10000.0}\")\n",
|
||||
"\n",
|
||||
" for s in all_sets:\n",
|
||||
" print(f\" {chemical_set[s][0]} = {solver.Value(set_vars[s]) / 1000.0}\",\n",
|
||||
" end=\" \")\n",
|
||||
" print(f\" {chemical_set[s][0]} = {solver.Value(set_vars[s]) / 1000.0}\", end=\" \")\n",
|
||||
" print()\n",
|
||||
" for p in all_products:\n",
|
||||
" name = max_quantities[p][0]\n",
|
||||
" max_quantity = max_quantities[p][1]\n",
|
||||
" quantity = sum(\n",
|
||||
" solver.Value(set_vars[s]) / 1000.0 * chemical_set[s][p + 1]\n",
|
||||
" for s in all_sets)\n",
|
||||
" for s in all_sets\n",
|
||||
" )\n",
|
||||
" print(f\"{name}: {quantity} out of {max_quantity}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Cluster 40 cities in 4 equal groups to minimize sum of crossed distances.\n"
|
||||
]
|
||||
},
|
||||
@@ -87,6 +88,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"distance_matrix = [\n",
|
||||
" # fmt:off\n",
|
||||
" [0, 10938, 4542, 2835, 29441, 2171, 1611, 9208, 9528, 11111, 16120, 22606, 22127, 20627, 21246, 23387, 16697, 33609, 26184, 24772, 22644, 20655, 30492, 23296, 32979, 18141, 19248, 17129, 17192, 15645, 12658, 11210, 12094, 13175, 18162, 4968, 12308, 10084, 13026, 15056],\n",
|
||||
" [10938, 0, 6422, 9742, 18988, 12974, 11216, 19715, 19004, 18271, 25070, 31971, 31632, 30571, 31578, 33841, 27315, 43964, 36944, 35689, 33569, 31481, 41360, 33760, 43631, 28730, 29976, 27803, 28076, 26408, 23504, 22025, 22000, 13197, 14936, 15146, 23246, 20956, 23963, 25994],\n",
|
||||
" [4542, 6422, 0, 3644, 25173, 6552, 5092, 13584, 13372, 13766, 19805, 26537, 26117, 24804, 25590, 27784, 21148, 37981, 30693, 29315, 27148, 25071, 34943, 27472, 37281, 22389, 23592, 21433, 21655, 20011, 17087, 15612, 15872, 11653, 15666, 8842, 16843, 14618, 17563, 19589],\n",
|
||||
@@ -127,13 +129,14 @@
|
||||
" [10084, 20956, 14618, 12135, 38935, 8306, 9793, 2615, 5850, 10467, 9918, 14568, 13907, 11803, 11750, 13657, 6901, 23862, 16125, 14748, 12981, 11624, 21033, 15358, 24144, 10304, 10742, 9094, 8042, 7408, 4580, 4072, 8446, 20543, 26181, 7668, 2747, 0, 3330, 5313],\n",
|
||||
" [13026, 23963, 17563, 14771, 42160, 11069, 12925, 5730, 8778, 13375, 11235, 14366, 13621, 11188, 10424, 11907, 5609, 21861, 13624, 11781, 9718, 8304, 17737, 12200, 20816, 7330, 7532, 6117, 4735, 4488, 2599, 3355, 7773, 22186, 27895, 9742, 726, 3330, 0, 2042],\n",
|
||||
" [15056, 25994, 19589, 16743, 44198, 13078, 14967, 7552, 10422, 14935, 11891, 14002, 13225, 10671, 9475, 10633, 5084, 20315, 11866, 9802, 7682, 6471, 15720, 10674, 18908, 6204, 6000, 5066, 3039, 3721, 3496, 4772, 8614, 23805, 29519, 11614, 2749, 5313, 2042, 0],\n",
|
||||
"] # yapf: disable\n",
|
||||
" # fmt:on\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def clustering_sat():\n",
|
||||
" \"\"\"Entry point of the program.\"\"\"\n",
|
||||
" num_nodes = len(distance_matrix)\n",
|
||||
" print('Num nodes =', num_nodes)\n",
|
||||
" print(\"Num nodes =\", num_nodes)\n",
|
||||
"\n",
|
||||
" # Number of groups to split the nodes, must divide num_nodes.\n",
|
||||
" num_groups = 4\n",
|
||||
@@ -148,7 +151,7 @@
|
||||
" obj_coeffs = []\n",
|
||||
" for n1 in range(num_nodes - 1):\n",
|
||||
" for n2 in range(n1 + 1, num_nodes):\n",
|
||||
" same = model.NewBoolVar('neighbors_%i_%i' % (n1, n2))\n",
|
||||
" same = model.NewBoolVar(\"neighbors_%i_%i\" % (n1, n2))\n",
|
||||
" neighbors[n1, n2] = same\n",
|
||||
" obj_vars.append(same)\n",
|
||||
" obj_coeffs.append(distance_matrix[n1][n2] + distance_matrix[n2][n1])\n",
|
||||
@@ -156,23 +159,24 @@
|
||||
" # Number of neighborss:\n",
|
||||
" for n in range(num_nodes):\n",
|
||||
" model.Add(\n",
|
||||
" sum(neighbors[m, n] for m in range(n)) +\n",
|
||||
" sum(neighbors[n, m]\n",
|
||||
" for m in range(n + 1, num_nodes)) == group_size - 1)\n",
|
||||
" sum(neighbors[m, n] for m in range(n))\n",
|
||||
" + sum(neighbors[n, m] for m in range(n + 1, num_nodes))\n",
|
||||
" == group_size - 1\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Enforce transivity on all triplets.\n",
|
||||
" for n1 in range(num_nodes - 2):\n",
|
||||
" for n2 in range(n1 + 1, num_nodes - 1):\n",
|
||||
" for n3 in range(n2 + 1, num_nodes):\n",
|
||||
" model.Add(neighbors[n1, n3] + neighbors[n2, n3] +\n",
|
||||
" neighbors[n1, n2] != 2)\n",
|
||||
" model.Add(\n",
|
||||
" neighbors[n1, n3] + neighbors[n2, n3] + neighbors[n1, n2] != 2\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Redundant constraints on total sum of neighborss.\n",
|
||||
" model.Add(sum(obj_vars) == num_groups * group_size * (group_size - 1) // 2)\n",
|
||||
"\n",
|
||||
" # Minimize weighted sum of arcs.\n",
|
||||
" model.Minimize(\n",
|
||||
" sum(obj_vars[i] * obj_coeffs[i] for i in range(len(obj_vars))))\n",
|
||||
" model.Minimize(sum(obj_vars[i] * obj_coeffs[i] for i in range(len(obj_vars))))\n",
|
||||
"\n",
|
||||
" # Solve and print out the solution.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
@@ -192,14 +196,14 @@
|
||||
" for o in range(n + 1, num_nodes):\n",
|
||||
" if solver.BooleanValue(neighbors[n, o]):\n",
|
||||
" visited.add(o)\n",
|
||||
" output += ' ' + str(o)\n",
|
||||
" print('Group', g, ':', output)\n",
|
||||
" output += \" \" + str(o)\n",
|
||||
" print(\"Group\", g, \":\", output)\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" clustering_sat()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Fill a 60x50 rectangle by a minimum number of non-overlapping squares.\n"
|
||||
]
|
||||
},
|
||||
@@ -102,16 +103,16 @@
|
||||
"\n",
|
||||
" # Creates intervals for the NoOverlap2D and size variables.\n",
|
||||
" for i in range(num_squares):\n",
|
||||
" size = model.NewIntVar(1, size_y, 'size_%i' % i)\n",
|
||||
" start_x = model.NewIntVar(0, size_x, 'sx_%i' % i)\n",
|
||||
" end_x = model.NewIntVar(0, size_x, 'ex_%i' % i)\n",
|
||||
" start_y = model.NewIntVar(0, size_y, 'sy_%i' % i)\n",
|
||||
" end_y = model.NewIntVar(0, size_y, 'ey_%i' % i)\n",
|
||||
" size = model.NewIntVar(1, size_y, \"size_%i\" % i)\n",
|
||||
" start_x = model.NewIntVar(0, size_x, \"sx_%i\" % i)\n",
|
||||
" end_x = model.NewIntVar(0, size_x, \"ex_%i\" % i)\n",
|
||||
" start_y = model.NewIntVar(0, size_y, \"sy_%i\" % i)\n",
|
||||
" end_y = model.NewIntVar(0, size_y, \"ey_%i\" % i)\n",
|
||||
"\n",
|
||||
" interval_x = model.NewIntervalVar(start_x, size, end_x, 'ix_%i' % i)\n",
|
||||
" interval_y = model.NewIntervalVar(start_y, size, end_y, 'iy_%i' % i)\n",
|
||||
" interval_x = model.NewIntervalVar(start_x, size, end_x, \"ix_%i\" % i)\n",
|
||||
" interval_y = model.NewIntervalVar(start_y, size, end_y, \"iy_%i\" % i)\n",
|
||||
"\n",
|
||||
" area = model.NewIntVar(1, size_y * size_y, 'area_%i' % i)\n",
|
||||
" area = model.NewIntVar(1, size_y * size_y, \"area_%i\" % i)\n",
|
||||
" model.AddMultiplicationEquality(area, [size, size])\n",
|
||||
"\n",
|
||||
" areas.append(area)\n",
|
||||
@@ -136,7 +137,7 @@
|
||||
" model.Add(sizes[i] <= sizes[i + 1])\n",
|
||||
"\n",
|
||||
" # Define same to be true iff sizes[i] == sizes[i + 1]\n",
|
||||
" same = model.NewBoolVar('')\n",
|
||||
" same = model.NewBoolVar(\"\")\n",
|
||||
" model.Add(sizes[i] == sizes[i + 1]).OnlyEnforceIf(same)\n",
|
||||
" model.Add(sizes[i] < sizes[i + 1]).OnlyEnforceIf(same.Not())\n",
|
||||
"\n",
|
||||
@@ -149,35 +150,39 @@
|
||||
"\n",
|
||||
" # Creates a solver and solves.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" solver.parameters.num_workers = 8\n",
|
||||
" solver.parameters.num_workers = 16\n",
|
||||
" # solver.parameters.log_search_progress = True\n",
|
||||
" solver.parameters.max_time_in_seconds = 10.0\n",
|
||||
" status = solver.Solve(model)\n",
|
||||
" print('%s found in %0.2fs' % (solver.StatusName(status), solver.WallTime()))\n",
|
||||
" print(\"%s found in %0.2fs\" % (solver.StatusName(status), solver.WallTime()))\n",
|
||||
"\n",
|
||||
" # Prints solution.\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" display = [[' ' for _ in range(size_x)] for _ in range(size_y)]\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" display = [[\" \" for _ in range(size_x)] for _ in range(size_y)]\n",
|
||||
" for i in range(num_squares):\n",
|
||||
" sol_x = solver.Value(x_starts[i])\n",
|
||||
" sol_y = solver.Value(y_starts[i])\n",
|
||||
" sol_s = solver.Value(sizes[i])\n",
|
||||
" char = format(i, '01x')\n",
|
||||
" char = format(i, \"01x\")\n",
|
||||
" for j in range(sol_s):\n",
|
||||
" for k in range(sol_s):\n",
|
||||
" if display[sol_y + j][sol_x + k] != ' ':\n",
|
||||
" print('ERROR between %s and %s' %\n",
|
||||
" (display[sol_y + j][sol_x + k], char))\n",
|
||||
" if display[sol_y + j][sol_x + k] != \" \":\n",
|
||||
" print(\n",
|
||||
" \"ERROR between %s and %s\"\n",
|
||||
" % (display[sol_y + j][sol_x + k], char)\n",
|
||||
" )\n",
|
||||
" display[sol_y + j][sol_x + k] = char\n",
|
||||
"\n",
|
||||
" for line in range(size_y):\n",
|
||||
" print(' '.join(display[line]))\n",
|
||||
" print(\" \".join(display[line]))\n",
|
||||
" return status == cp_model.OPTIMAL\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" for num_squares in range(1, 15):\n",
|
||||
" print('Trying with size =', num_squares)\n",
|
||||
" print(\"Trying with size =\", num_squares)\n",
|
||||
" if cover_rectangle(num_squares):\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Use CP-SAT to solve a simple cryptarithmetic problem: SEND+MORE=MONEY.\n",
|
||||
"\n"
|
||||
]
|
||||
@@ -87,28 +88,27 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"def send_more_money():\n",
|
||||
" \"\"\"Solve the cryptarithmic puzzle SEND+MORE=MONEY.\n",
|
||||
" \"\"\"\n",
|
||||
" \"\"\"Solve the cryptarithmic puzzle SEND+MORE=MONEY.\"\"\"\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
" # Create variables.\n",
|
||||
" # Since s is a leading digit, it can't be 0.\n",
|
||||
" s = model.NewIntVar(1, 9, 's')\n",
|
||||
" e = model.NewIntVar(0, 9, 'e')\n",
|
||||
" n = model.NewIntVar(0, 9, 'n')\n",
|
||||
" d = model.NewIntVar(0, 9, 'd')\n",
|
||||
" s = model.NewIntVar(1, 9, \"s\")\n",
|
||||
" e = model.NewIntVar(0, 9, \"e\")\n",
|
||||
" n = model.NewIntVar(0, 9, \"n\")\n",
|
||||
" d = model.NewIntVar(0, 9, \"d\")\n",
|
||||
" # Since m is a leading digit, it can't be 0.\n",
|
||||
" m = model.NewIntVar(1, 9, 'm')\n",
|
||||
" o = model.NewIntVar(0, 9, 'o')\n",
|
||||
" r = model.NewIntVar(0, 9, 'r')\n",
|
||||
" y = model.NewIntVar(0, 9, 'y')\n",
|
||||
" m = model.NewIntVar(1, 9, \"m\")\n",
|
||||
" o = model.NewIntVar(0, 9, \"o\")\n",
|
||||
" r = model.NewIntVar(0, 9, \"r\")\n",
|
||||
" y = model.NewIntVar(0, 9, \"y\")\n",
|
||||
"\n",
|
||||
" # Create carry variables. c0 is true if the first column of addends carries\n",
|
||||
" # a 1, c2 is true if the second column carries a 1, and so on.\n",
|
||||
" c0 = model.NewBoolVar('c0')\n",
|
||||
" c1 = model.NewBoolVar('c1')\n",
|
||||
" c2 = model.NewBoolVar('c2')\n",
|
||||
" c3 = model.NewBoolVar('c3')\n",
|
||||
" c0 = model.NewBoolVar(\"c0\")\n",
|
||||
" c1 = model.NewBoolVar(\"c1\")\n",
|
||||
" c2 = model.NewBoolVar(\"c2\")\n",
|
||||
" c3 = model.NewBoolVar(\"c3\")\n",
|
||||
"\n",
|
||||
" # Force all letters to take on different values.\n",
|
||||
" model.AddAllDifferent(s, e, n, d, m, o, r, y)\n",
|
||||
@@ -131,15 +131,15 @@
|
||||
" # Solve model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" if solver.Solve(model) == cp_model.OPTIMAL:\n",
|
||||
" print('Optimal solution found!')\n",
|
||||
" print('s:', solver.Value(s))\n",
|
||||
" print('e:', solver.Value(e))\n",
|
||||
" print('n:', solver.Value(n))\n",
|
||||
" print('d:', solver.Value(d))\n",
|
||||
" print('m:', solver.Value(m))\n",
|
||||
" print('o:', solver.Value(o))\n",
|
||||
" print('r:', solver.Value(r))\n",
|
||||
" print('y:', solver.Value(y))\n",
|
||||
" print(\"Optimal solution found!\")\n",
|
||||
" print(\"s:\", solver.Value(s))\n",
|
||||
" print(\"e:\", solver.Value(e))\n",
|
||||
" print(\"n:\", solver.Value(n))\n",
|
||||
" print(\"d:\", solver.Value(d))\n",
|
||||
" print(\"m:\", solver.Value(m))\n",
|
||||
" print(\"o:\", solver.Value(o))\n",
|
||||
" print(\"r:\", solver.Value(r))\n",
|
||||
" print(\"y:\", solver.Value(y))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(_):\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solves a flexible jobshop problems with the CP-SAT solver.\n",
|
||||
"\n",
|
||||
"A jobshop is a standard scheduling problem when you must sequence a\n",
|
||||
@@ -106,8 +107,10 @@
|
||||
"\n",
|
||||
" def on_solution_callback(self):\n",
|
||||
" \"\"\"Called at each new solution.\"\"\"\n",
|
||||
" print('Solution %i, time = %f s, objective = %i' %\n",
|
||||
" (self.__solution_count, self.WallTime(), self.ObjectiveValue()))\n",
|
||||
" print(\n",
|
||||
" \"Solution %i, time = %f s, objective = %i\"\n",
|
||||
" % (self.__solution_count, self.WallTime(), self.ObjectiveValue())\n",
|
||||
" )\n",
|
||||
" self.__solution_count += 1\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -149,7 +152,7 @@
|
||||
" max_task_duration = max(max_task_duration, alternative[0])\n",
|
||||
" horizon += max_task_duration\n",
|
||||
"\n",
|
||||
" print('Horizon = %i' % horizon)\n",
|
||||
" print(\"Horizon = %i\" % horizon)\n",
|
||||
"\n",
|
||||
" # Global storage of variables.\n",
|
||||
" intervals_per_resources = collections.defaultdict(list)\n",
|
||||
@@ -177,13 +180,15 @@
|
||||
" max_duration = max(max_duration, alt_duration)\n",
|
||||
"\n",
|
||||
" # Create main interval for the task.\n",
|
||||
" suffix_name = '_j%i_t%i' % (job_id, task_id)\n",
|
||||
" start = model.NewIntVar(0, horizon, 'start' + suffix_name)\n",
|
||||
" duration = model.NewIntVar(min_duration, max_duration,\n",
|
||||
" 'duration' + suffix_name)\n",
|
||||
" end = model.NewIntVar(0, horizon, 'end' + suffix_name)\n",
|
||||
" interval = model.NewIntervalVar(start, duration, end,\n",
|
||||
" 'interval' + suffix_name)\n",
|
||||
" suffix_name = \"_j%i_t%i\" % (job_id, task_id)\n",
|
||||
" start = model.NewIntVar(0, horizon, \"start\" + suffix_name)\n",
|
||||
" duration = model.NewIntVar(\n",
|
||||
" min_duration, max_duration, \"duration\" + suffix_name\n",
|
||||
" )\n",
|
||||
" end = model.NewIntVar(0, horizon, \"end\" + suffix_name)\n",
|
||||
" interval = model.NewIntervalVar(\n",
|
||||
" start, duration, end, \"interval\" + suffix_name\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Store the start for the solution.\n",
|
||||
" starts[(job_id, task_id)] = start\n",
|
||||
@@ -197,14 +202,14 @@
|
||||
" if num_alternatives > 1:\n",
|
||||
" l_presences = []\n",
|
||||
" for alt_id in all_alternatives:\n",
|
||||
" alt_suffix = '_j%i_t%i_a%i' % (job_id, task_id, alt_id)\n",
|
||||
" l_presence = model.NewBoolVar('presence' + alt_suffix)\n",
|
||||
" l_start = model.NewIntVar(0, horizon, 'start' + alt_suffix)\n",
|
||||
" alt_suffix = \"_j%i_t%i_a%i\" % (job_id, task_id, alt_id)\n",
|
||||
" l_presence = model.NewBoolVar(\"presence\" + alt_suffix)\n",
|
||||
" l_start = model.NewIntVar(0, horizon, \"start\" + alt_suffix)\n",
|
||||
" l_duration = task[alt_id][0]\n",
|
||||
" l_end = model.NewIntVar(0, horizon, 'end' + alt_suffix)\n",
|
||||
" l_end = model.NewIntVar(0, horizon, \"end\" + alt_suffix)\n",
|
||||
" l_interval = model.NewOptionalIntervalVar(\n",
|
||||
" l_start, l_duration, l_end, l_presence,\n",
|
||||
" 'interval' + alt_suffix)\n",
|
||||
" l_start, l_duration, l_end, l_presence, \"interval\" + alt_suffix\n",
|
||||
" )\n",
|
||||
" l_presences.append(l_presence)\n",
|
||||
"\n",
|
||||
" # Link the primary/global variables with the local ones.\n",
|
||||
@@ -233,7 +238,7 @@
|
||||
" model.AddNoOverlap(intervals)\n",
|
||||
"\n",
|
||||
" # Makespan objective\n",
|
||||
" makespan = model.NewIntVar(0, horizon, 'makespan')\n",
|
||||
" makespan = model.NewIntVar(0, horizon, \"makespan\")\n",
|
||||
" model.AddMaxEquality(makespan, job_ends)\n",
|
||||
" model.Minimize(makespan)\n",
|
||||
"\n",
|
||||
@@ -244,7 +249,7 @@
|
||||
"\n",
|
||||
" # Print final solution.\n",
|
||||
" for job_id in all_jobs:\n",
|
||||
" print('Job %i:' % job_id)\n",
|
||||
" print(\"Job %i:\" % job_id)\n",
|
||||
" for task_id in range(len(jobs[job_id])):\n",
|
||||
" start_value = solver.Value(starts[(job_id, task_id)])\n",
|
||||
" machine = -1\n",
|
||||
@@ -256,15 +261,16 @@
|
||||
" machine = jobs[job_id][task_id][alt_id][1]\n",
|
||||
" selected = alt_id\n",
|
||||
" print(\n",
|
||||
" ' task_%i_%i starts at %i (alt %i, machine %i, duration %i)' %\n",
|
||||
" (job_id, task_id, start_value, selected, machine, duration))\n",
|
||||
" \" task_%i_%i starts at %i (alt %i, machine %i, duration %i)\"\n",
|
||||
" % (job_id, task_id, start_value, selected, machine, duration)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" print('Solve status: %s' % solver.StatusName(status))\n",
|
||||
" print('Optimal objective value: %i' % solver.ObjectiveValue())\n",
|
||||
" print('Statistics')\n",
|
||||
" print(' - conflicts : %i' % solver.NumConflicts())\n",
|
||||
" print(' - branches : %i' % solver.NumBranches())\n",
|
||||
" print(' - wall time : %f s' % solver.WallTime())\n",
|
||||
" print(\"Solve status: %s\" % solver.StatusName(status))\n",
|
||||
" print(\"Optimal objective value: %i\" % solver.ObjectiveValue())\n",
|
||||
" print(\"Statistics\")\n",
|
||||
" print(\" - conflicts : %i\" % solver.NumConflicts())\n",
|
||||
" print(\" - branches : %i\" % solver.NumBranches())\n",
|
||||
" print(\" - wall time : %f s\" % solver.WallTime())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"flexible_jobshop()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Gate Scheduling problem.\n",
|
||||
"\n",
|
||||
"We have a set of jobs to perform (duration, width).\n",
|
||||
@@ -114,7 +115,7 @@
|
||||
" [1, 2],\n",
|
||||
" [6, 8],\n",
|
||||
" [4, 5],\n",
|
||||
" [3, 7]\n",
|
||||
" [3, 7],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" max_width = 10\n",
|
||||
@@ -133,31 +134,31 @@
|
||||
"\n",
|
||||
" for i in all_jobs:\n",
|
||||
" # Create main interval.\n",
|
||||
" start = model.NewIntVar(0, horizon, 'start_%i' % i)\n",
|
||||
" start = model.NewIntVar(0, horizon, \"start_%i\" % i)\n",
|
||||
" duration = jobs[i][0]\n",
|
||||
" end = model.NewIntVar(0, horizon, 'end_%i' % i)\n",
|
||||
" interval = model.NewIntervalVar(start, duration, end, 'interval_%i' % i)\n",
|
||||
" end = model.NewIntVar(0, horizon, \"end_%i\" % i)\n",
|
||||
" interval = model.NewIntervalVar(start, duration, end, \"interval_%i\" % i)\n",
|
||||
" starts.append(start)\n",
|
||||
" intervals.append(interval)\n",
|
||||
" ends.append(end)\n",
|
||||
" demands.append(jobs[i][1])\n",
|
||||
"\n",
|
||||
" # Create an optional copy of interval to be executed on machine 0.\n",
|
||||
" performed_on_m0 = model.NewBoolVar('perform_%i_on_m0' % i)\n",
|
||||
" performed_on_m0 = model.NewBoolVar(\"perform_%i_on_m0\" % i)\n",
|
||||
" performed.append(performed_on_m0)\n",
|
||||
" start0 = model.NewIntVar(0, horizon, 'start_%i_on_m0' % i)\n",
|
||||
" end0 = model.NewIntVar(0, horizon, 'end_%i_on_m0' % i)\n",
|
||||
" interval0 = model.NewOptionalIntervalVar(start0, duration, end0,\n",
|
||||
" performed_on_m0,\n",
|
||||
" 'interval_%i_on_m0' % i)\n",
|
||||
" start0 = model.NewIntVar(0, horizon, \"start_%i_on_m0\" % i)\n",
|
||||
" end0 = model.NewIntVar(0, horizon, \"end_%i_on_m0\" % i)\n",
|
||||
" interval0 = model.NewOptionalIntervalVar(\n",
|
||||
" start0, duration, end0, performed_on_m0, \"interval_%i_on_m0\" % i\n",
|
||||
" )\n",
|
||||
" intervals0.append(interval0)\n",
|
||||
"\n",
|
||||
" # Create an optional copy of interval to be executed on machine 1.\n",
|
||||
" start1 = model.NewIntVar(0, horizon, 'start_%i_on_m1' % i)\n",
|
||||
" end1 = model.NewIntVar(0, horizon, 'end_%i_on_m1' % i)\n",
|
||||
" interval1 = model.NewOptionalIntervalVar(start1, duration, end1,\n",
|
||||
" performed_on_m0.Not(),\n",
|
||||
" 'interval_%i_on_m1' % i)\n",
|
||||
" start1 = model.NewIntVar(0, horizon, \"start_%i_on_m1\" % i)\n",
|
||||
" end1 = model.NewIntVar(0, horizon, \"end_%i_on_m1\" % i)\n",
|
||||
" interval1 = model.NewOptionalIntervalVar(\n",
|
||||
" start1, duration, end1, performed_on_m0.Not(), \"interval_%i_on_m1\" % i\n",
|
||||
" )\n",
|
||||
" intervals1.append(interval1)\n",
|
||||
"\n",
|
||||
" # We only propagate the constraint if the tasks is performed on the machine.\n",
|
||||
@@ -172,7 +173,7 @@
|
||||
" model.AddNoOverlap(intervals1)\n",
|
||||
"\n",
|
||||
" # Objective variable.\n",
|
||||
" makespan = model.NewIntVar(0, horizon, 'makespan')\n",
|
||||
" makespan = model.NewIntVar(0, horizon, \"makespan\")\n",
|
||||
" model.AddMaxEquality(makespan, ends)\n",
|
||||
" model.Minimize(makespan)\n",
|
||||
"\n",
|
||||
@@ -185,9 +186,8 @@
|
||||
"\n",
|
||||
" # Output solution.\n",
|
||||
" if visualization.RunFromIPython():\n",
|
||||
" output = visualization.SvgWrapper(solver.ObjectiveValue(), max_width,\n",
|
||||
" 40.0)\n",
|
||||
" output.AddTitle('Makespan = %i' % solver.ObjectiveValue())\n",
|
||||
" output = visualization.SvgWrapper(solver.ObjectiveValue(), max_width, 40.0)\n",
|
||||
" output.AddTitle(\"Makespan = %i\" % solver.ObjectiveValue())\n",
|
||||
" color_manager = visualization.ColorManager()\n",
|
||||
" color_manager.SeedRandomColor(0)\n",
|
||||
"\n",
|
||||
@@ -197,24 +197,26 @@
|
||||
" d_x = jobs[i][0]\n",
|
||||
" d_y = jobs[i][1]\n",
|
||||
" s_y = performed_machine * (max_width - d_y)\n",
|
||||
" output.AddRectangle(start, s_y, d_x, d_y,\n",
|
||||
" color_manager.RandomColor(), 'black', 'j%i' % i)\n",
|
||||
" output.AddRectangle(\n",
|
||||
" start, s_y, d_x, d_y, color_manager.RandomColor(), \"black\", \"j%i\" % i\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" output.AddXScale()\n",
|
||||
" output.AddYScale()\n",
|
||||
" output.Display()\n",
|
||||
" else:\n",
|
||||
" print('Solution')\n",
|
||||
" print(' - makespan = %i' % solver.ObjectiveValue())\n",
|
||||
" print(\"Solution\")\n",
|
||||
" print(\" - makespan = %i\" % solver.ObjectiveValue())\n",
|
||||
" for i in all_jobs:\n",
|
||||
" performed_machine = 1 - solver.Value(performed[i])\n",
|
||||
" start = solver.Value(starts[i])\n",
|
||||
" print(' - Job %i starts at %i on machine %i' %\n",
|
||||
" (i, start, performed_machine))\n",
|
||||
" print('Statistics')\n",
|
||||
" print(' - conflicts : %i' % solver.NumConflicts())\n",
|
||||
" print(' - branches : %i' % solver.NumBranches())\n",
|
||||
" print(' - wall time : %f s' % solver.WallTime())\n",
|
||||
" print(\n",
|
||||
" \" - Job %i starts at %i on machine %i\" % (i, start, performed_machine)\n",
|
||||
" )\n",
|
||||
" print(\"Statistics\")\n",
|
||||
" print(\" - conflicts : %i\" % solver.NumConflicts())\n",
|
||||
" print(\" - branches : %i\" % solver.NumBranches())\n",
|
||||
" print(\" - wall time : %f s\" % solver.WallTime())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"This is the Golomb ruler problem.\n",
|
||||
"\n",
|
||||
"This model aims at maximizing radar interferences in a minimum space.\n",
|
||||
@@ -96,13 +97,13 @@
|
||||
"\n",
|
||||
"def main(_):\n",
|
||||
" # Create the solver.\n",
|
||||
" solver = pywrapcp.Solver('golomb ruler')\n",
|
||||
" solver = pywrapcp.Solver(\"golomb ruler\")\n",
|
||||
"\n",
|
||||
" size = 8\n",
|
||||
" var_max = size * size\n",
|
||||
" all_vars = list(range(0, size))\n",
|
||||
"\n",
|
||||
" marks = [solver.IntVar(0, var_max, 'marks_%d' % i) for i in all_vars]\n",
|
||||
" marks = [solver.IntVar(0, var_max, \"marks_%d\" % i) for i in all_vars]\n",
|
||||
"\n",
|
||||
" objective = solver.Minimize(marks[size - 1], 1)\n",
|
||||
"\n",
|
||||
@@ -124,20 +125,27 @@
|
||||
" collector = solver.AllSolutionCollector(solution)\n",
|
||||
"\n",
|
||||
" solver.Solve(\n",
|
||||
" solver.Phase(marks, solver.CHOOSE_FIRST_UNBOUND,\n",
|
||||
" solver.ASSIGN_MIN_VALUE), [objective, collector])\n",
|
||||
" solver.Phase(marks, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE),\n",
|
||||
" [objective, collector],\n",
|
||||
" )\n",
|
||||
" for i in range(0, collector.SolutionCount()):\n",
|
||||
" obj_value = collector.Value(i, marks[size - 1])\n",
|
||||
" time = collector.WallTime(i)\n",
|
||||
" branches = collector.Branches(i)\n",
|
||||
" failures = collector.Failures(i)\n",
|
||||
" print(('Solution #%i: value = %i, failures = %i, branches = %i,'\n",
|
||||
" 'time = %i ms') % (i, obj_value, failures, branches, time))\n",
|
||||
" print(\n",
|
||||
" (\"Solution #%i: value = %i, failures = %i, branches = %i,\" \"time = %i ms\")\n",
|
||||
" % (i, obj_value, failures, branches, time)\n",
|
||||
" )\n",
|
||||
" time = solver.WallTime()\n",
|
||||
" branches = solver.Branches()\n",
|
||||
" failures = solver.Failures()\n",
|
||||
" print(('Total run : failures = %i, branches = %i, time = %i ms' %\n",
|
||||
" (failures, branches, time)))\n",
|
||||
" print(\n",
|
||||
" (\n",
|
||||
" \"Total run : failures = %i, branches = %i, time = %i ms\"\n",
|
||||
" % (failures, branches, time)\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"This is the Golomb ruler problem.\n",
|
||||
"\n",
|
||||
"This model aims at maximizing radar interferences in a minimum space.\n",
|
||||
@@ -95,11 +96,12 @@
|
||||
"from google.protobuf import text_format\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"_ORDER = flags.DEFINE_integer('order', 8, 'Order of the ruler.')\n",
|
||||
"_ORDER = flags.DEFINE_integer(\"order\", 8, \"Order of the ruler.\")\n",
|
||||
"_PARAMS = flags.DEFINE_string(\n",
|
||||
" 'params',\n",
|
||||
" 'num_search_workers:16,log_search_progress:true,max_time_in_seconds:45',\n",
|
||||
" 'Sat solver parameters.')\n",
|
||||
" \"params\",\n",
|
||||
" \"num_search_workers:16,log_search_progress:true,max_time_in_seconds:45\",\n",
|
||||
" \"Sat solver parameters.\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def solve_golomb_ruler(order, params):\n",
|
||||
@@ -110,7 +112,7 @@
|
||||
" var_max = order * order\n",
|
||||
" all_vars = list(range(0, order))\n",
|
||||
"\n",
|
||||
" marks = [model.NewIntVar(0, var_max, f'marks_{i}') for i in all_vars]\n",
|
||||
" marks = [model.NewIntVar(0, var_max, f\"marks_{i}\") for i in all_vars]\n",
|
||||
"\n",
|
||||
" model.Add(marks[0] == 0)\n",
|
||||
" for i in range(order - 2):\n",
|
||||
@@ -119,7 +121,7 @@
|
||||
" diffs = []\n",
|
||||
" for i in range(order - 1):\n",
|
||||
" for j in range(i + 1, order):\n",
|
||||
" diff = model.NewIntVar(0, var_max, f'diff [{j},{i}]')\n",
|
||||
" diff = model.NewIntVar(0, var_max, f\"diff [{j},{i}]\")\n",
|
||||
" model.Add(diff == marks[j] - marks[i])\n",
|
||||
" diffs.append(diff)\n",
|
||||
" model.AddAllDifferent(diffs)\n",
|
||||
@@ -136,27 +138,27 @@
|
||||
" if params:\n",
|
||||
" text_format.Parse(params, solver.parameters)\n",
|
||||
" solution_printer = cp_model.ObjectiveSolutionPrinter()\n",
|
||||
" print(f'Golomb ruler(order={order})')\n",
|
||||
" print(f\"Golomb ruler(order={order})\")\n",
|
||||
" status = solver.Solve(model, solution_printer)\n",
|
||||
"\n",
|
||||
" # Print solution.\n",
|
||||
" print(f'status: {solver.StatusName(status)}')\n",
|
||||
" print(f\"status: {solver.StatusName(status)}\")\n",
|
||||
" if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):\n",
|
||||
" for idx, var in enumerate(marks):\n",
|
||||
" print(f'mark[{idx}]: {solver.Value(var)}')\n",
|
||||
" print(f\"mark[{idx}]: {solver.Value(var)}\")\n",
|
||||
" intervals = [solver.Value(diff) for diff in diffs]\n",
|
||||
" intervals.sort()\n",
|
||||
" print(f'intervals: {intervals}')\n",
|
||||
" print(f\"intervals: {intervals}\")\n",
|
||||
"\n",
|
||||
" print('Statistics:')\n",
|
||||
" print(f'- conflicts: {solver.NumConflicts()}')\n",
|
||||
" print(f'- branches : {solver.NumBranches()}')\n",
|
||||
" print(f'- wall time: {solver.WallTime()}s\\n')\n",
|
||||
" print(\"Statistics:\")\n",
|
||||
" print(f\"- conflicts: {solver.NumConflicts()}\")\n",
|
||||
" print(f\"- branches : {solver.NumBranches()}\")\n",
|
||||
" print(f\"- wall time: {solver.WallTime()}s\\n\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" solve_golomb_ruler(_ORDER.value, _PARAMS.value)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solves the Hidato problem with the CP-SAT solver.\n"
|
||||
]
|
||||
},
|
||||
@@ -89,25 +90,29 @@
|
||||
"def build_pairs(rows, cols):\n",
|
||||
" \"\"\"Build closeness pairs for consecutive numbers.\n",
|
||||
"\n",
|
||||
" Build set of allowed pairs such that two consecutive numbers touch\n",
|
||||
" each other in the grid.\n",
|
||||
" Build set of allowed pairs such that two consecutive numbers touch\n",
|
||||
" each other in the grid.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" A list of pairs for allowed consecutive position of numbers.\n",
|
||||
" Returns:\n",
|
||||
" A list of pairs for allowed consecutive position of numbers.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" rows: the number of rows in the grid\n",
|
||||
" cols: the number of columns in the grid\n",
|
||||
" \"\"\"\n",
|
||||
" Args:\n",
|
||||
" rows: the number of rows in the grid\n",
|
||||
" cols: the number of columns in the grid\n",
|
||||
" \"\"\"\n",
|
||||
" result = []\n",
|
||||
" for x in range(rows):\n",
|
||||
" for y in range(cols):\n",
|
||||
" for dx in (-1, 0, 1):\n",
|
||||
" for dy in (-1, 0, 1):\n",
|
||||
" if (x + dx >= 0 and x + dx < rows and y + dy >= 0 and\n",
|
||||
" y + dy < cols and (dx != 0 or dy != 0)):\n",
|
||||
" result.append(\n",
|
||||
" (x * cols + y, (x + dx) * cols + (y + dy)))\n",
|
||||
" if (\n",
|
||||
" x + dx >= 0\n",
|
||||
" and x + dx < rows\n",
|
||||
" and y + dy >= 0\n",
|
||||
" and y + dy < cols\n",
|
||||
" and (dx != 0 or dy != 0)\n",
|
||||
" ):\n",
|
||||
" result.append((x * cols + y, (x + dx) * cols + (y + dy)))\n",
|
||||
" return result\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -122,7 +127,7 @@
|
||||
" position = positions[k]\n",
|
||||
" board[position // cols][position % cols] = k + 1\n",
|
||||
" # Print the board.\n",
|
||||
" print('Solution')\n",
|
||||
" print(\"Solution\")\n",
|
||||
" print_matrix(board)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -131,12 +136,12 @@
|
||||
" rows = len(game)\n",
|
||||
" cols = len(game[0])\n",
|
||||
" for i in range(rows):\n",
|
||||
" line = ''\n",
|
||||
" line = \"\"\n",
|
||||
" for j in range(cols):\n",
|
||||
" if game[i][j] == 0:\n",
|
||||
" line += ' .'\n",
|
||||
" line += \" .\"\n",
|
||||
" else:\n",
|
||||
" line += '% 3s' % game[i][j]\n",
|
||||
" line += \"% 3s\" % game[i][j]\n",
|
||||
" print(line)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -152,34 +157,60 @@
|
||||
" puzzle = [[6, 0, 9], [0, 2, 8], [1, 0, 0]]\n",
|
||||
"\n",
|
||||
" elif problem == 2:\n",
|
||||
" puzzle = [[0, 44, 41, 0, 0, 0, 0], [0, 43, 0, 28, 29, 0, 0],\n",
|
||||
" [0, 1, 0, 0, 0, 33, 0], [0, 2, 25, 4, 34, 0, 36],\n",
|
||||
" [49, 16, 0, 23, 0, 0, 0], [0, 19, 0, 0, 12, 7, 0],\n",
|
||||
" [0, 0, 0, 14, 0, 0, 0]]\n",
|
||||
" puzzle = [\n",
|
||||
" [0, 44, 41, 0, 0, 0, 0],\n",
|
||||
" [0, 43, 0, 28, 29, 0, 0],\n",
|
||||
" [0, 1, 0, 0, 0, 33, 0],\n",
|
||||
" [0, 2, 25, 4, 34, 0, 36],\n",
|
||||
" [49, 16, 0, 23, 0, 0, 0],\n",
|
||||
" [0, 19, 0, 0, 12, 7, 0],\n",
|
||||
" [0, 0, 0, 14, 0, 0, 0],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" elif problem == 3:\n",
|
||||
" # Problems from the book:\n",
|
||||
" # Gyora Bededek: \"Hidato: 2000 Pure Logic Puzzles\"\n",
|
||||
" # Problem 1 (Practice)\n",
|
||||
" puzzle = [[0, 0, 20, 0, 0], [0, 0, 0, 16, 18], [22, 0, 15, 0, 0],\n",
|
||||
" [23, 0, 1, 14, 11], [0, 25, 0, 0, 12]]\n",
|
||||
" puzzle = [\n",
|
||||
" [0, 0, 20, 0, 0],\n",
|
||||
" [0, 0, 0, 16, 18],\n",
|
||||
" [22, 0, 15, 0, 0],\n",
|
||||
" [23, 0, 1, 14, 11],\n",
|
||||
" [0, 25, 0, 0, 12],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" elif problem == 4:\n",
|
||||
" # problem 2 (Practice)\n",
|
||||
" puzzle = [[0, 0, 0, 0, 14], [0, 18, 12, 0, 0], [0, 0, 17, 4, 5],\n",
|
||||
" [0, 0, 7, 0, 0], [9, 8, 25, 1, 0]]\n",
|
||||
" puzzle = [\n",
|
||||
" [0, 0, 0, 0, 14],\n",
|
||||
" [0, 18, 12, 0, 0],\n",
|
||||
" [0, 0, 17, 4, 5],\n",
|
||||
" [0, 0, 7, 0, 0],\n",
|
||||
" [9, 8, 25, 1, 0],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" elif problem == 5:\n",
|
||||
" # problem 3 (Beginner)\n",
|
||||
" puzzle = [[0, 26, 0, 0, 0, 18], [0, 0, 27, 0, 0, 19],\n",
|
||||
" [31, 23, 0, 0, 14, 0], [0, 33, 8, 0, 15, 1],\n",
|
||||
" [0, 0, 0, 5, 0, 0], [35, 36, 0, 10, 0, 0]]\n",
|
||||
" puzzle = [\n",
|
||||
" [0, 26, 0, 0, 0, 18],\n",
|
||||
" [0, 0, 27, 0, 0, 19],\n",
|
||||
" [31, 23, 0, 0, 14, 0],\n",
|
||||
" [0, 33, 8, 0, 15, 1],\n",
|
||||
" [0, 0, 0, 5, 0, 0],\n",
|
||||
" [35, 36, 0, 10, 0, 0],\n",
|
||||
" ]\n",
|
||||
" elif problem == 6:\n",
|
||||
" # Problem 15 (Intermediate)\n",
|
||||
" puzzle = [[64, 0, 0, 0, 0, 0, 0, 0], [1, 63, 0, 59, 15, 57, 53, 0],\n",
|
||||
" [0, 4, 0, 14, 0, 0, 0, 0], [3, 0, 11, 0, 20, 19, 0, 50],\n",
|
||||
" [0, 0, 0, 0, 22, 0, 48, 40], [9, 0, 0, 32, 23, 0, 0, 41],\n",
|
||||
" [27, 0, 0, 0, 36, 0, 46, 0], [28, 30, 0, 35, 0, 0, 0, 0]]\n",
|
||||
" puzzle = [\n",
|
||||
" [64, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 63, 0, 59, 15, 57, 53, 0],\n",
|
||||
" [0, 4, 0, 14, 0, 0, 0, 0],\n",
|
||||
" [3, 0, 11, 0, 20, 19, 0, 50],\n",
|
||||
" [0, 0, 0, 0, 22, 0, 48, 40],\n",
|
||||
" [9, 0, 0, 32, 23, 0, 0, 41],\n",
|
||||
" [27, 0, 0, 0, 36, 0, 46, 0],\n",
|
||||
" [28, 30, 0, 35, 0, 0, 0, 0],\n",
|
||||
" ]\n",
|
||||
" return puzzle\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -191,18 +222,16 @@
|
||||
" r = len(puzzle)\n",
|
||||
" c = len(puzzle[0])\n",
|
||||
" if not visualization.RunFromIPython():\n",
|
||||
" print('')\n",
|
||||
" print('----- Solving problem %i -----' % index)\n",
|
||||
" print('')\n",
|
||||
" print(('Initial game (%i x %i)' % (r, c)))\n",
|
||||
" print(\"\")\n",
|
||||
" print(\"----- Solving problem %i -----\" % index)\n",
|
||||
" print(\"\")\n",
|
||||
" print((\"Initial game (%i x %i)\" % (r, c)))\n",
|
||||
" print_matrix(puzzle)\n",
|
||||
"\n",
|
||||
" #\n",
|
||||
" # declare variables\n",
|
||||
" #\n",
|
||||
" positions = [\n",
|
||||
" model.NewIntVar(0, r * c - 1, 'p[%i]' % i) for i in range(r * c)\n",
|
||||
" ]\n",
|
||||
" positions = [model.NewIntVar(0, r * c - 1, \"p[%i]\" % i) for i in range(r * c)]\n",
|
||||
"\n",
|
||||
" #\n",
|
||||
" # constraints\n",
|
||||
@@ -221,8 +250,7 @@
|
||||
" # We use an allowed assignment constraint to model it.\n",
|
||||
" close_tuples = build_pairs(r, c)\n",
|
||||
" for k in range(0, r * c - 1):\n",
|
||||
" model.AddAllowedAssignments([positions[k], positions[k + 1]],\n",
|
||||
" close_tuples)\n",
|
||||
" model.AddAllowedAssignments([positions[k], positions[k + 1]], close_tuples)\n",
|
||||
"\n",
|
||||
" #\n",
|
||||
" # solution and search\n",
|
||||
@@ -238,12 +266,10 @@
|
||||
" val = solver.Value(var)\n",
|
||||
" x = val % c\n",
|
||||
" y = val // c\n",
|
||||
" color = 'white' if puzzle[y][x] == 0 else 'lightgreen'\n",
|
||||
" output.AddRectangle(x, r - y - 1, 1, 1, color, 'black',\n",
|
||||
" str(i + 1))\n",
|
||||
" color = \"white\" if puzzle[y][x] == 0 else \"lightgreen\"\n",
|
||||
" output.AddRectangle(x, r - y - 1, 1, 1, color, \"black\", str(i + 1))\n",
|
||||
"\n",
|
||||
" output.AddTitle('Puzzle %i solved in %f s' %\n",
|
||||
" (index, solver.WallTime()))\n",
|
||||
" output.AddTitle(\"Puzzle %i solved in %f s\" % (index, solver.WallTime()))\n",
|
||||
" output.Display()\n",
|
||||
" else:\n",
|
||||
" print_solution(\n",
|
||||
@@ -251,10 +277,10 @@
|
||||
" r,\n",
|
||||
" c,\n",
|
||||
" )\n",
|
||||
" print('Statistics')\n",
|
||||
" print(' - conflicts : %i' % solver.NumConflicts())\n",
|
||||
" print(' - branches : %i' % solver.NumBranches())\n",
|
||||
" print(' - wall time : %f s' % solver.WallTime())\n",
|
||||
" print(\"Statistics\")\n",
|
||||
" print(\" - conflicts : %i\" % solver.NumConflicts())\n",
|
||||
" print(\" - branches : %i\" % solver.NumBranches())\n",
|
||||
" print(\" - wall time : %f s\" % solver.WallTime())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(_):\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Integer programming examples that show how to use the APIs.\n"
|
||||
]
|
||||
},
|
||||
@@ -83,12 +84,12 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.linear_solver import pywraplp\n",
|
||||
"from ortools.init import pywrapinit\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def Announce(solver, api_type):\n",
|
||||
" print('---- Integer programming example with ' + solver + ' (' + api_type +\n",
|
||||
" ') -----')\n",
|
||||
" print(\n",
|
||||
" \"---- Integer programming example with \" + solver + \" (\" + api_type + \") -----\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def RunIntegerExampleNaturalLanguageAPI(optimization_problem_type):\n",
|
||||
@@ -98,12 +99,12 @@
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" Announce(optimization_problem_type, 'natural language API')\n",
|
||||
" Announce(optimization_problem_type, \"natural language API\")\n",
|
||||
"\n",
|
||||
" infinity = solver.infinity()\n",
|
||||
" # x1 and x2 are integer non-negative variables.\n",
|
||||
" x1 = solver.IntVar(0.0, infinity, 'x1')\n",
|
||||
" x2 = solver.IntVar(0.0, infinity, 'x2')\n",
|
||||
" x1 = solver.IntVar(0.0, infinity, \"x1\")\n",
|
||||
" x2 = solver.IntVar(0.0, infinity, \"x2\")\n",
|
||||
"\n",
|
||||
" solver.Minimize(x1 + 2 * x2)\n",
|
||||
" solver.Add(3 * x1 + 2 * x2 >= 17)\n",
|
||||
@@ -117,12 +118,12 @@
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" Announce(optimization_problem_type, 'C++ style API')\n",
|
||||
" Announce(optimization_problem_type, \"C++ style API\")\n",
|
||||
"\n",
|
||||
" infinity = solver.infinity()\n",
|
||||
" # x1 and x2 are integer non-negative variables.\n",
|
||||
" x1 = solver.IntVar(0.0, infinity, 'x1')\n",
|
||||
" x2 = solver.IntVar(0.0, infinity, 'x2')\n",
|
||||
" x1 = solver.IntVar(0.0, infinity, \"x1\")\n",
|
||||
" x2 = solver.IntVar(0.0, infinity, \"x2\")\n",
|
||||
"\n",
|
||||
" # Minimize x1 + 2 * x2.\n",
|
||||
" objective = solver.Objective()\n",
|
||||
@@ -139,8 +140,8 @@
|
||||
"\n",
|
||||
"def SolveAndPrint(solver, variable_list):\n",
|
||||
" \"\"\"Solve the problem and print the solution.\"\"\"\n",
|
||||
" print('Number of variables = %d' % solver.NumVariables())\n",
|
||||
" print('Number of constraints = %d' % solver.NumConstraints())\n",
|
||||
" print(\"Number of variables = %d\" % solver.NumVariables())\n",
|
||||
" print(\"Number of constraints = %d\" % solver.NumConstraints())\n",
|
||||
"\n",
|
||||
" result_status = solver.Solve()\n",
|
||||
"\n",
|
||||
@@ -151,33 +152,33 @@
|
||||
" # GLOP_LINEAR_PROGRAMMING, verifying the solution is highly recommended!).\n",
|
||||
" assert solver.VerifySolution(1e-7, True)\n",
|
||||
"\n",
|
||||
" print('Problem solved in %f milliseconds' % solver.wall_time())\n",
|
||||
" print(\"Problem solved in %f milliseconds\" % solver.wall_time())\n",
|
||||
"\n",
|
||||
" # The objective value of the solution.\n",
|
||||
" print('Optimal objective value = %f' % solver.Objective().Value())\n",
|
||||
" print(\"Optimal objective value = %f\" % solver.Objective().Value())\n",
|
||||
"\n",
|
||||
" # The value of each variable in the solution.\n",
|
||||
" for variable in variable_list:\n",
|
||||
" print('%s = %f' % (variable.name(), variable.solution_value()))\n",
|
||||
" print(\"%s = %f\" % (variable.name(), variable.solution_value()))\n",
|
||||
"\n",
|
||||
" print('Advanced usage:')\n",
|
||||
" print('Problem solved in %d branch-and-bound nodes' % solver.nodes())\n",
|
||||
" print(\"Advanced usage:\")\n",
|
||||
" print(\"Problem solved in %d branch-and-bound nodes\" % solver.nodes())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def RunAllIntegerExampleNaturalLanguageAPI():\n",
|
||||
" RunIntegerExampleNaturalLanguageAPI('GLPK')\n",
|
||||
" RunIntegerExampleNaturalLanguageAPI('CBC')\n",
|
||||
" RunIntegerExampleNaturalLanguageAPI('SCIP')\n",
|
||||
" RunIntegerExampleNaturalLanguageAPI('SAT')\n",
|
||||
" RunIntegerExampleNaturalLanguageAPI('Gurobi')\n",
|
||||
" RunIntegerExampleNaturalLanguageAPI(\"GLPK\")\n",
|
||||
" # Disabling due to ASAN errors with CBC.\n",
|
||||
" # RunIntegerExampleNaturalLanguageAPI('CBC')\n",
|
||||
" RunIntegerExampleNaturalLanguageAPI(\"SCIP\")\n",
|
||||
" RunIntegerExampleNaturalLanguageAPI(\"SAT\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def RunAllIntegerExampleCppStyleAPI():\n",
|
||||
" RunIntegerExampleCppStyleAPI('GLPK')\n",
|
||||
" RunIntegerExampleCppStyleAPI('CBC')\n",
|
||||
" RunIntegerExampleCppStyleAPI('SCIP')\n",
|
||||
" RunIntegerExampleCppStyleAPI('SAT')\n",
|
||||
" RunIntegerExampleCppStyleAPI('Gurobi')\n",
|
||||
" RunIntegerExampleCppStyleAPI(\"GLPK\")\n",
|
||||
" # Disabling due to ASAN errors with CBC.\n",
|
||||
" # RunIntegerExampleCppStyleAPI('CBC')\n",
|
||||
" RunIntegerExampleCppStyleAPI(\"SCIP\")\n",
|
||||
" RunIntegerExampleCppStyleAPI(\"SAT\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
@@ -185,11 +186,6 @@
|
||||
" RunAllIntegerExampleCppStyleAPI()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"pywrapinit.CppBridge.InitLogging('integer_programming.py')\n",
|
||||
"cpp_flags = pywrapinit.CppFlags()\n",
|
||||
"cpp_flags.stderrthreshold = 0\n",
|
||||
"cpp_flags.log_prefix = False\n",
|
||||
"pywrapinit.CppBridge.SetFlags(cpp_flags)\n",
|
||||
"main()\n",
|
||||
"\n"
|
||||
]
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"This model implements a variation of the ft06 jobshop.\n",
|
||||
"\n",
|
||||
"A jobshop is a standard scheduling problem when you must sequence a\n",
|
||||
@@ -114,29 +115,42 @@
|
||||
" all_machines = range(0, machines_count)\n",
|
||||
" all_jobs = range(0, jobs_count)\n",
|
||||
"\n",
|
||||
" durations = [[1, 3, 6, 7, 3, 6], [8, 5, 10, 10, 10, 4], [5, 4, 8, 9, 1, 7],\n",
|
||||
" [5, 5, 5, 3, 8, 9], [9, 3, 5, 4, 3, 1], [3, 3, 9, 10, 4, 1]]\n",
|
||||
" durations = [\n",
|
||||
" [1, 3, 6, 7, 3, 6],\n",
|
||||
" [8, 5, 10, 10, 10, 4],\n",
|
||||
" [5, 4, 8, 9, 1, 7],\n",
|
||||
" [5, 5, 5, 3, 8, 9],\n",
|
||||
" [9, 3, 5, 4, 3, 1],\n",
|
||||
" [3, 3, 9, 10, 4, 1],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" machines = [[2, 0, 1, 3, 5, 4], [1, 2, 4, 5, 0, 3], [2, 3, 5, 0, 1, 4],\n",
|
||||
" [1, 0, 2, 3, 4, 5], [2, 1, 4, 5, 0, 3], [1, 3, 5, 0, 4, 2]]\n",
|
||||
" machines = [\n",
|
||||
" [2, 0, 1, 3, 5, 4],\n",
|
||||
" [1, 2, 4, 5, 0, 3],\n",
|
||||
" [2, 3, 5, 0, 1, 4],\n",
|
||||
" [1, 0, 2, 3, 4, 5],\n",
|
||||
" [2, 1, 4, 5, 0, 3],\n",
|
||||
" [1, 3, 5, 0, 4, 2],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" # Computes horizon statically.\n",
|
||||
" horizon = 150\n",
|
||||
"\n",
|
||||
" task_type = collections.namedtuple('task_type', 'start end interval')\n",
|
||||
" task_type = collections.namedtuple(\"task_type\", \"start end interval\")\n",
|
||||
"\n",
|
||||
" # Creates jobs.\n",
|
||||
" all_tasks = {}\n",
|
||||
" for i in all_jobs:\n",
|
||||
" for j in all_machines:\n",
|
||||
" start_var = model.NewIntVar(0, horizon, 'start_%i_%i' % (i, j))\n",
|
||||
" start_var = model.NewIntVar(0, horizon, \"start_%i_%i\" % (i, j))\n",
|
||||
" duration = durations[i][j]\n",
|
||||
" end_var = model.NewIntVar(0, horizon, 'end_%i_%i' % (i, j))\n",
|
||||
" interval_var = model.NewIntervalVar(start_var, duration, end_var,\n",
|
||||
" 'interval_%i_%i' % (i, j))\n",
|
||||
" all_tasks[(i, j)] = task_type(start=start_var,\n",
|
||||
" end=end_var,\n",
|
||||
" interval=interval_var)\n",
|
||||
" end_var = model.NewIntVar(0, horizon, \"end_%i_%i\" % (i, j))\n",
|
||||
" interval_var = model.NewIntervalVar(\n",
|
||||
" start_var, duration, end_var, \"interval_%i_%i\" % (i, j)\n",
|
||||
" )\n",
|
||||
" all_tasks[(i, j)] = task_type(\n",
|
||||
" start=start_var, end=end_var, interval=interval_var\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create disjuctive constraints.\n",
|
||||
" for i in all_machines:\n",
|
||||
@@ -156,23 +170,24 @@
|
||||
" arcs = []\n",
|
||||
" for j1 in range(len(job_intervals)):\n",
|
||||
" # Initial arc from the dummy node (0) to a task.\n",
|
||||
" start_lit = model.NewBoolVar('%i is first job' % j1)\n",
|
||||
" arcs.append([0, j1 + 1, start_lit])\n",
|
||||
" start_lit = model.NewBoolVar(\"%i is first job\" % j1)\n",
|
||||
" arcs.append((0, j1 + 1, start_lit))\n",
|
||||
" # Final arc from an arc to the dummy node.\n",
|
||||
" arcs.append([j1 + 1, 0, model.NewBoolVar('%i is last job' % j1)])\n",
|
||||
" arcs.append((j1 + 1, 0, model.NewBoolVar(\"%i is last job\" % j1)))\n",
|
||||
"\n",
|
||||
" for j2 in range(len(job_intervals)):\n",
|
||||
" if j1 == j2:\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" lit = model.NewBoolVar('%i follows %i' % (j2, j1))\n",
|
||||
" arcs.append([j1 + 1, j2 + 1, lit])\n",
|
||||
" lit = model.NewBoolVar(\"%i follows %i\" % (j2, j1))\n",
|
||||
" arcs.append((j1 + 1, j2 + 1, lit))\n",
|
||||
"\n",
|
||||
" # We add the reified precedence to link the literal with the\n",
|
||||
" # times of the two tasks.\n",
|
||||
" min_distance = distance_between_jobs(j1, j2)\n",
|
||||
" model.Add(job_starts[j2] >= job_ends[j1] +\n",
|
||||
" min_distance).OnlyEnforceIf(lit)\n",
|
||||
" model.Add(job_starts[j2] >= job_ends[j1] + min_distance).OnlyEnforceIf(\n",
|
||||
" lit\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" model.AddCircuit(arcs)\n",
|
||||
"\n",
|
||||
@@ -182,9 +197,10 @@
|
||||
" model.Add(all_tasks[(i, j + 1)].start >= all_tasks[(i, j)].end)\n",
|
||||
"\n",
|
||||
" # Makespan objective.\n",
|
||||
" obj_var = model.NewIntVar(0, horizon, 'makespan')\n",
|
||||
" obj_var = model.NewIntVar(0, horizon, \"makespan\")\n",
|
||||
" model.AddMaxEquality(\n",
|
||||
" obj_var, [all_tasks[(i, machines_count - 1)].end for i in all_jobs])\n",
|
||||
" obj_var, [all_tasks[(i, machines_count - 1)].end for i in all_jobs]\n",
|
||||
" )\n",
|
||||
" model.Minimize(obj_var)\n",
|
||||
"\n",
|
||||
" # Solve model.\n",
|
||||
@@ -193,7 +209,7 @@
|
||||
"\n",
|
||||
" # Output solution.\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" print('Optimal makespan: %i' % solver.ObjectiveValue())\n",
|
||||
" print(\"Optimal makespan: %i\" % solver.ObjectiveValue())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"jobshop_ft06_distance()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"This model implements a simple jobshop named ft06.\n",
|
||||
"\n",
|
||||
"A jobshop is a standard scheduling problem when you must sequence a\n",
|
||||
@@ -107,29 +108,42 @@
|
||||
" all_machines = range(0, machines_count)\n",
|
||||
" all_jobs = range(0, jobs_count)\n",
|
||||
"\n",
|
||||
" durations = [[1, 3, 6, 7, 3, 6], [8, 5, 10, 10, 10, 4], [5, 4, 8, 9, 1, 7],\n",
|
||||
" [5, 5, 5, 3, 8, 9], [9, 3, 5, 4, 3, 1], [3, 3, 9, 10, 4, 1]]\n",
|
||||
" durations = [\n",
|
||||
" [1, 3, 6, 7, 3, 6],\n",
|
||||
" [8, 5, 10, 10, 10, 4],\n",
|
||||
" [5, 4, 8, 9, 1, 7],\n",
|
||||
" [5, 5, 5, 3, 8, 9],\n",
|
||||
" [9, 3, 5, 4, 3, 1],\n",
|
||||
" [3, 3, 9, 10, 4, 1],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" machines = [[2, 0, 1, 3, 5, 4], [1, 2, 4, 5, 0, 3], [2, 3, 5, 0, 1, 4],\n",
|
||||
" [1, 0, 2, 3, 4, 5], [2, 1, 4, 5, 0, 3], [1, 3, 5, 0, 4, 2]]\n",
|
||||
" machines = [\n",
|
||||
" [2, 0, 1, 3, 5, 4],\n",
|
||||
" [1, 2, 4, 5, 0, 3],\n",
|
||||
" [2, 3, 5, 0, 1, 4],\n",
|
||||
" [1, 0, 2, 3, 4, 5],\n",
|
||||
" [2, 1, 4, 5, 0, 3],\n",
|
||||
" [1, 3, 5, 0, 4, 2],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" # Computes horizon dynamically.\n",
|
||||
" horizon = sum([sum(durations[i]) for i in all_jobs])\n",
|
||||
"\n",
|
||||
" task_type = collections.namedtuple('task_type', 'start end interval')\n",
|
||||
" task_type = collections.namedtuple(\"task_type\", \"start end interval\")\n",
|
||||
"\n",
|
||||
" # Creates jobs.\n",
|
||||
" all_tasks = {}\n",
|
||||
" for i in all_jobs:\n",
|
||||
" for j in all_machines:\n",
|
||||
" start_var = model.NewIntVar(0, horizon, 'start_%i_%i' % (i, j))\n",
|
||||
" start_var = model.NewIntVar(0, horizon, \"start_%i_%i\" % (i, j))\n",
|
||||
" duration = durations[i][j]\n",
|
||||
" end_var = model.NewIntVar(0, horizon, 'end_%i_%i' % (i, j))\n",
|
||||
" interval_var = model.NewIntervalVar(start_var, duration, end_var,\n",
|
||||
" 'interval_%i_%i' % (i, j))\n",
|
||||
" all_tasks[(i, j)] = task_type(start=start_var,\n",
|
||||
" end=end_var,\n",
|
||||
" interval=interval_var)\n",
|
||||
" end_var = model.NewIntVar(0, horizon, \"end_%i_%i\" % (i, j))\n",
|
||||
" interval_var = model.NewIntervalVar(\n",
|
||||
" start_var, duration, end_var, \"interval_%i_%i\" % (i, j)\n",
|
||||
" )\n",
|
||||
" all_tasks[(i, j)] = task_type(\n",
|
||||
" start=start_var, end=end_var, interval=interval_var\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create disjuctive constraints.\n",
|
||||
" machine_to_jobs = {}\n",
|
||||
@@ -148,9 +162,10 @@
|
||||
" model.Add(all_tasks[(i, j + 1)].start >= all_tasks[(i, j)].end)\n",
|
||||
"\n",
|
||||
" # Makespan objective.\n",
|
||||
" obj_var = model.NewIntVar(0, horizon, 'makespan')\n",
|
||||
" obj_var = model.NewIntVar(0, horizon, \"makespan\")\n",
|
||||
" model.AddMaxEquality(\n",
|
||||
" obj_var, [all_tasks[(i, machines_count - 1)].end for i in all_jobs])\n",
|
||||
" obj_var, [all_tasks[(i, machines_count - 1)].end for i in all_jobs]\n",
|
||||
" )\n",
|
||||
" model.Minimize(obj_var)\n",
|
||||
"\n",
|
||||
" # Solve model.\n",
|
||||
@@ -161,12 +176,13 @@
|
||||
" # Output solution.\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" if visualization.RunFromIPython():\n",
|
||||
" starts = [[\n",
|
||||
" solver.Value(all_tasks[(i, j)][0]) for j in all_machines\n",
|
||||
" ] for i in all_jobs]\n",
|
||||
" visualization.DisplayJobshop(starts, durations, machines, 'FT06')\n",
|
||||
" starts = [\n",
|
||||
" [solver.Value(all_tasks[(i, j)][0]) for j in all_machines]\n",
|
||||
" for i in all_jobs\n",
|
||||
" ]\n",
|
||||
" visualization.DisplayJobshop(starts, durations, machines, \"FT06\")\n",
|
||||
" else:\n",
|
||||
" print('Optimal makespan: %i' % solver.ObjectiveValue())\n",
|
||||
" print(\"Optimal makespan: %i\" % solver.ObjectiveValue())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"jobshop_ft06()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Jobshop with maintenance tasks using the CP-SAT solver.\n"
|
||||
]
|
||||
},
|
||||
@@ -96,8 +97,10 @@
|
||||
"\n",
|
||||
" def on_solution_callback(self):\n",
|
||||
" \"\"\"Called at each new solution.\"\"\"\n",
|
||||
" print('Solution %i, time = %f s, objective = %i' %\n",
|
||||
" (self.__solution_count, self.WallTime(), self.ObjectiveValue()))\n",
|
||||
" print(\n",
|
||||
" \"Solution %i, time = %f s, objective = %i\"\n",
|
||||
" % (self.__solution_count, self.WallTime(), self.ObjectiveValue())\n",
|
||||
" )\n",
|
||||
" self.__solution_count += 1\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -119,10 +122,11 @@
|
||||
" horizon = sum(task[1] for job in jobs_data for task in job)\n",
|
||||
"\n",
|
||||
" # Named tuple to store information about created variables.\n",
|
||||
" task_type = collections.namedtuple('Task', 'start end interval')\n",
|
||||
" task_type = collections.namedtuple(\"Task\", \"start end interval\")\n",
|
||||
" # Named tuple to manipulate solution information.\n",
|
||||
" assigned_task_type = collections.namedtuple('assigned_task_type',\n",
|
||||
" 'start job index duration')\n",
|
||||
" assigned_task_type = collections.namedtuple(\n",
|
||||
" \"assigned_task_type\", \"start job index duration\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Creates job intervals and add to the corresponding machine lists.\n",
|
||||
" all_tasks = {}\n",
|
||||
@@ -132,18 +136,19 @@
|
||||
" for task_id, task in enumerate(job):\n",
|
||||
" machine = task[0]\n",
|
||||
" duration = task[1]\n",
|
||||
" suffix = '_%i_%i' % (job_id, task_id)\n",
|
||||
" start_var = model.NewIntVar(0, horizon, 'start' + suffix)\n",
|
||||
" end_var = model.NewIntVar(0, horizon, 'end' + suffix)\n",
|
||||
" interval_var = model.NewIntervalVar(start_var, duration, end_var,\n",
|
||||
" 'interval' + suffix)\n",
|
||||
" all_tasks[job_id, task_id] = task_type(start=start_var,\n",
|
||||
" end=end_var,\n",
|
||||
" interval=interval_var)\n",
|
||||
" suffix = \"_%i_%i\" % (job_id, task_id)\n",
|
||||
" start_var = model.NewIntVar(0, horizon, \"start\" + suffix)\n",
|
||||
" end_var = model.NewIntVar(0, horizon, \"end\" + suffix)\n",
|
||||
" interval_var = model.NewIntervalVar(\n",
|
||||
" start_var, duration, end_var, \"interval\" + suffix\n",
|
||||
" )\n",
|
||||
" all_tasks[job_id, task_id] = task_type(\n",
|
||||
" start=start_var, end=end_var, interval=interval_var\n",
|
||||
" )\n",
|
||||
" machine_to_intervals[machine].append(interval_var)\n",
|
||||
"\n",
|
||||
" # Add maintenance interval (machine 0 is not available on time {4, 5, 6, 7}).\n",
|
||||
" machine_to_intervals[0].append(model.NewIntervalVar(4, 4, 8, 'weekend_0'))\n",
|
||||
" machine_to_intervals[0].append(model.NewIntervalVar(4, 4, 8, \"weekend_0\"))\n",
|
||||
"\n",
|
||||
" # Create and add disjunctive constraints.\n",
|
||||
" for machine in all_machines:\n",
|
||||
@@ -152,15 +157,16 @@
|
||||
" # Precedences inside a job.\n",
|
||||
" for job_id, job in enumerate(jobs_data):\n",
|
||||
" for task_id in range(len(job) - 1):\n",
|
||||
" model.Add(all_tasks[job_id, task_id +\n",
|
||||
" 1].start >= all_tasks[job_id, task_id].end)\n",
|
||||
" model.Add(\n",
|
||||
" all_tasks[job_id, task_id + 1].start >= all_tasks[job_id, task_id].end\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Makespan objective.\n",
|
||||
" obj_var = model.NewIntVar(0, horizon, 'makespan')\n",
|
||||
" model.AddMaxEquality(obj_var, [\n",
|
||||
" all_tasks[job_id, len(job) - 1].end\n",
|
||||
" for job_id, job in enumerate(jobs_data)\n",
|
||||
" ])\n",
|
||||
" obj_var = model.NewIntVar(0, horizon, \"makespan\")\n",
|
||||
" model.AddMaxEquality(\n",
|
||||
" obj_var,\n",
|
||||
" [all_tasks[job_id, len(job) - 1].end for job_id, job in enumerate(jobs_data)],\n",
|
||||
" )\n",
|
||||
" model.Minimize(obj_var)\n",
|
||||
"\n",
|
||||
" # Solve model.\n",
|
||||
@@ -176,48 +182,50 @@
|
||||
" for task_id, task in enumerate(job):\n",
|
||||
" machine = task[0]\n",
|
||||
" assigned_jobs[machine].append(\n",
|
||||
" assigned_task_type(start=solver.Value(\n",
|
||||
" all_tasks[job_id, task_id].start),\n",
|
||||
" job=job_id,\n",
|
||||
" index=task_id,\n",
|
||||
" duration=task[1]))\n",
|
||||
" assigned_task_type(\n",
|
||||
" start=solver.Value(all_tasks[job_id, task_id].start),\n",
|
||||
" job=job_id,\n",
|
||||
" index=task_id,\n",
|
||||
" duration=task[1],\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create per machine output lines.\n",
|
||||
" output = ''\n",
|
||||
" output = \"\"\n",
|
||||
" for machine in all_machines:\n",
|
||||
" # Sort by starting time.\n",
|
||||
" assigned_jobs[machine].sort()\n",
|
||||
" sol_line_tasks = 'Machine ' + str(machine) + ': '\n",
|
||||
" sol_line = ' '\n",
|
||||
" sol_line_tasks = \"Machine \" + str(machine) + \": \"\n",
|
||||
" sol_line = \" \"\n",
|
||||
"\n",
|
||||
" for assigned_task in assigned_jobs[machine]:\n",
|
||||
" name = 'job_%i_%i' % (assigned_task.job, assigned_task.index)\n",
|
||||
" name = \"job_%i_%i\" % (assigned_task.job, assigned_task.index)\n",
|
||||
" # Add spaces to output to align columns.\n",
|
||||
" sol_line_tasks += '%-10s' % name\n",
|
||||
" sol_line_tasks += \"%-10s\" % name\n",
|
||||
" start = assigned_task.start\n",
|
||||
" duration = assigned_task.duration\n",
|
||||
"\n",
|
||||
" sol_tmp = '[%i,%i]' % (start, start + duration)\n",
|
||||
" sol_tmp = \"[%i,%i]\" % (start, start + duration)\n",
|
||||
" # Add spaces to output to align columns.\n",
|
||||
" sol_line += '%-10s' % sol_tmp\n",
|
||||
" sol_line += \"%-10s\" % sol_tmp\n",
|
||||
"\n",
|
||||
" sol_line += '\\n'\n",
|
||||
" sol_line_tasks += '\\n'\n",
|
||||
" sol_line += \"\\n\"\n",
|
||||
" sol_line_tasks += \"\\n\"\n",
|
||||
" output += sol_line_tasks\n",
|
||||
" output += sol_line\n",
|
||||
"\n",
|
||||
" # Finally print the solution found.\n",
|
||||
" print('Optimal Schedule Length: %i' % solver.ObjectiveValue())\n",
|
||||
" print(\"Optimal Schedule Length: %i\" % solver.ObjectiveValue())\n",
|
||||
" print(output)\n",
|
||||
" print('Statistics')\n",
|
||||
" print(' - conflicts : %i' % solver.NumConflicts())\n",
|
||||
" print(' - branches : %i' % solver.NumBranches())\n",
|
||||
" print(' - wall time : %f s' % solver.WallTime())\n",
|
||||
" print(\"Statistics\")\n",
|
||||
" print(\" - conflicts : %i\" % solver.NumConflicts())\n",
|
||||
" print(\" - branches : %i\" % solver.NumBranches())\n",
|
||||
" print(\" - wall time : %f s\" % solver.WallTime())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" jobshop_with_maintenance()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solver a 2D rectangle knapsack problem.\n",
|
||||
"\n",
|
||||
"This code is adapted from\n",
|
||||
@@ -96,14 +97,16 @@
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"_OUTPUT_PROTO = flags.DEFINE_string(\n",
|
||||
" 'output_proto', '', 'Output file to write the cp_model proto to.')\n",
|
||||
"_PARAMS = flags.DEFINE_string(\n",
|
||||
" 'params',\n",
|
||||
" 'num_search_workers:16,log_search_progress:true,max_time_in_seconds:45',\n",
|
||||
" 'Sat solver parameters.',\n",
|
||||
" \"output_proto\", \"\", \"Output file to write the cp_model proto to.\"\n",
|
||||
")\n",
|
||||
"_PARAMS = flags.DEFINE_string(\n",
|
||||
" \"params\",\n",
|
||||
" \"num_search_workers:16,log_search_progress:true,max_time_in_seconds:45\",\n",
|
||||
" \"Sat solver parameters.\",\n",
|
||||
")\n",
|
||||
"_MODEL = flags.DEFINE_string(\n",
|
||||
" \"model\", \"rotation\", \"'duplicate' or 'rotation' or 'optional'\"\n",
|
||||
")\n",
|
||||
"_MODEL = flags.DEFINE_string('model', 'rotation',\n",
|
||||
" '\\'duplicate\\' or \\'rotation\\' or \\'optional\\'')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def build_data():\n",
|
||||
@@ -122,25 +125,25 @@
|
||||
" k10 9 11 5 369.560 cyan\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" data = pd.read_table(io.StringIO(data), sep=r'\\s+')\n",
|
||||
" print('Input data')\n",
|
||||
" data = pd.read_table(io.StringIO(data), sep=r\"\\s+\")\n",
|
||||
" print(\"Input data\")\n",
|
||||
" print(data)\n",
|
||||
"\n",
|
||||
" max_height = 20\n",
|
||||
" max_width = 30\n",
|
||||
"\n",
|
||||
" print(f'Container max_width:{max_width} max_height:{max_height}')\n",
|
||||
" print(f'#Items: {len(data.index)}')\n",
|
||||
" print(f\"Container max_width:{max_width} max_height:{max_height}\")\n",
|
||||
" print(f\"#Items: {len(data.index)}\")\n",
|
||||
" return (data, max_height, max_width)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def solve_with_duplicate_items(data, max_height, max_width):\n",
|
||||
" \"\"\"Solve the problem by building 2 items (rotated or not) for each item.\"\"\"\n",
|
||||
" # Derived data (expanded to individual items).\n",
|
||||
" data_widths = data['width'].to_numpy()\n",
|
||||
" data_heights = data['height'].to_numpy()\n",
|
||||
" data_availability = data['available'].to_numpy()\n",
|
||||
" data_values = data['value'].to_numpy()\n",
|
||||
" data_widths = data[\"width\"].to_numpy()\n",
|
||||
" data_heights = data[\"height\"].to_numpy()\n",
|
||||
" data_availability = data[\"available\"].to_numpy()\n",
|
||||
" data_values = data[\"value\"].to_numpy()\n",
|
||||
"\n",
|
||||
" # Non duplicated items data.\n",
|
||||
" base_item_widths = np.repeat(data_widths, data_availability)\n",
|
||||
@@ -169,21 +172,29 @@
|
||||
"\n",
|
||||
" for i in range(num_items):\n",
|
||||
" ## Is the item used?\n",
|
||||
" is_used.append(model.NewBoolVar(f'is_used{i}'))\n",
|
||||
" is_used.append(model.NewBoolVar(f\"is_used{i}\"))\n",
|
||||
"\n",
|
||||
" ## Item coordinates.\n",
|
||||
" x_starts.append(model.NewIntVar(0, max_width, f'x_start{i}'))\n",
|
||||
" x_ends.append(model.NewIntVar(0, max_width, f'x_end{i}'))\n",
|
||||
" y_starts.append(model.NewIntVar(0, max_height, f'y_start{i}'))\n",
|
||||
" y_ends.append(model.NewIntVar(0, max_height, f'y_end{i}'))\n",
|
||||
" x_starts.append(model.NewIntVar(0, max_width, f\"x_start{i}\"))\n",
|
||||
" x_ends.append(model.NewIntVar(0, max_width, f\"x_end{i}\"))\n",
|
||||
" y_starts.append(model.NewIntVar(0, max_height, f\"y_start{i}\"))\n",
|
||||
" y_ends.append(model.NewIntVar(0, max_height, f\"y_end{i}\"))\n",
|
||||
"\n",
|
||||
" ## Interval variables.\n",
|
||||
" x_intervals.append(\n",
|
||||
" model.NewIntervalVar(x_starts[i], item_widths[i] * is_used[i],\n",
|
||||
" x_ends[i], f'x_interval{i}'))\n",
|
||||
" model.NewIntervalVar(\n",
|
||||
" x_starts[i], item_widths[i] * is_used[i], x_ends[i], f\"x_interval{i}\"\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
" y_intervals.append(\n",
|
||||
" model.NewIntervalVar(y_starts[i], item_heights[i] * is_used[i],\n",
|
||||
" y_ends[i], f'y_interval{i}'))\n",
|
||||
" model.NewIntervalVar(\n",
|
||||
" y_starts[i], item_heights[i] * is_used[i], y_ends[i], f\"y_interval{i}\"\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Unused boxes are fixed at (0.0).\n",
|
||||
" model.Add(x_starts[i] == 0).OnlyEnforceIf(is_used[i].Not())\n",
|
||||
" model.Add(y_starts[i] == 0).OnlyEnforceIf(is_used[i].Not())\n",
|
||||
"\n",
|
||||
" # Constraints.\n",
|
||||
"\n",
|
||||
@@ -199,8 +210,8 @@
|
||||
"\n",
|
||||
" # Output proto to file.\n",
|
||||
" if _OUTPUT_PROTO.value:\n",
|
||||
" print('Writing proto to %s' % _OUTPUT_PROTO.value)\n",
|
||||
" with open(_OUTPUT_PROTO.value, 'w') as text_file:\n",
|
||||
" print(f\"Writing proto to {_OUTPUT_PROTO.value}\")\n",
|
||||
" with open(_OUTPUT_PROTO.value, \"w\") as text_file:\n",
|
||||
" text_file.write(str(model))\n",
|
||||
"\n",
|
||||
" # Solve model.\n",
|
||||
@@ -211,27 +222,29 @@
|
||||
" status = solver.Solve(model)\n",
|
||||
"\n",
|
||||
" # Report solution.\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" used = {i for i in range(num_items) if solver.BooleanValue(is_used[i])}\n",
|
||||
" data = pd.DataFrame({\n",
|
||||
" 'x_start': [solver.Value(x_starts[i]) for i in used],\n",
|
||||
" 'y_start': [solver.Value(y_starts[i]) for i in used],\n",
|
||||
" 'item_width': [item_widths[i] for i in used],\n",
|
||||
" 'item_height': [item_heights[i] for i in used],\n",
|
||||
" 'x_end': [solver.Value(x_ends[i]) for i in used],\n",
|
||||
" 'y_end': [solver.Value(y_ends[i]) for i in used],\n",
|
||||
" 'item_value': [item_values[i] for i in used]\n",
|
||||
" })\n",
|
||||
" data = pd.DataFrame(\n",
|
||||
" {\n",
|
||||
" \"x_start\": [solver.Value(x_starts[i]) for i in used],\n",
|
||||
" \"y_start\": [solver.Value(y_starts[i]) for i in used],\n",
|
||||
" \"item_width\": [item_widths[i] for i in used],\n",
|
||||
" \"item_height\": [item_heights[i] for i in used],\n",
|
||||
" \"x_end\": [solver.Value(x_ends[i]) for i in used],\n",
|
||||
" \"y_end\": [solver.Value(y_ends[i]) for i in used],\n",
|
||||
" \"item_value\": [item_values[i] for i in used],\n",
|
||||
" }\n",
|
||||
" )\n",
|
||||
" print(data)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def solve_with_duplicate_optional_items(data, max_height, max_width):\n",
|
||||
" \"\"\"Solve the problem by building 2 optional items (rotated or not) for each item.\"\"\"\n",
|
||||
" # Derived data (expanded to individual items).\n",
|
||||
" data_widths = data['width'].to_numpy()\n",
|
||||
" data_heights = data['height'].to_numpy()\n",
|
||||
" data_availability = data['available'].to_numpy()\n",
|
||||
" data_values = data['value'].to_numpy()\n",
|
||||
" data_widths = data[\"width\"].to_numpy()\n",
|
||||
" data_heights = data[\"height\"].to_numpy()\n",
|
||||
" data_availability = data[\"available\"].to_numpy()\n",
|
||||
" data_values = data[\"value\"].to_numpy()\n",
|
||||
"\n",
|
||||
" # Non duplicated items data.\n",
|
||||
" base_item_widths = np.repeat(data_widths, data_availability)\n",
|
||||
@@ -258,22 +271,30 @@
|
||||
"\n",
|
||||
" for i in range(num_items):\n",
|
||||
" ## Is the item used?\n",
|
||||
" is_used.append(model.NewBoolVar(f'is_used{i}'))\n",
|
||||
" is_used.append(model.NewBoolVar(f\"is_used{i}\"))\n",
|
||||
"\n",
|
||||
" ## Item coordinates.\n",
|
||||
" x_starts.append(\n",
|
||||
" model.NewIntVar(0, max_width - int(item_widths[i]), f'x_start{i}'))\n",
|
||||
" model.NewIntVar(0, max_width - int(item_widths[i]), f\"x_start{i}\")\n",
|
||||
" )\n",
|
||||
" y_starts.append(\n",
|
||||
" model.NewIntVar(0, max_height - int(item_heights[i]),\n",
|
||||
" f'y_start{i}'))\n",
|
||||
" model.NewIntVar(0, max_height - int(item_heights[i]), f\"y_start{i}\")\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" ## Interval variables.\n",
|
||||
" x_intervals.append(\n",
|
||||
" model.NewOptionalFixedSizeIntervalVar(x_starts[i], item_widths[i],\n",
|
||||
" is_used[i], f'x_interval{i}'))\n",
|
||||
" model.NewOptionalFixedSizeIntervalVar(\n",
|
||||
" x_starts[i], item_widths[i], is_used[i], f\"x_interval{i}\"\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
" y_intervals.append(\n",
|
||||
" model.NewOptionalFixedSizeIntervalVar(y_starts[i], item_heights[i],\n",
|
||||
" is_used[i], f'y_interval{i}'))\n",
|
||||
" model.NewOptionalFixedSizeIntervalVar(\n",
|
||||
" y_starts[i], item_heights[i], is_used[i], f\"y_interval{i}\"\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
" # Unused boxes are fixed at (0.0).\n",
|
||||
" model.Add(x_starts[i] == 0).OnlyEnforceIf(is_used[i].Not())\n",
|
||||
" model.Add(y_starts[i] == 0).OnlyEnforceIf(is_used[i].Not())\n",
|
||||
"\n",
|
||||
" # Constraints.\n",
|
||||
"\n",
|
||||
@@ -289,8 +310,8 @@
|
||||
"\n",
|
||||
" # Output proto to file.\n",
|
||||
" if _OUTPUT_PROTO.value:\n",
|
||||
" print('Writing proto to %s' % _OUTPUT_PROTO.value)\n",
|
||||
" with open(_OUTPUT_PROTO.value, 'w') as text_file:\n",
|
||||
" print(f\"Writing proto to {_OUTPUT_PROTO.value}\")\n",
|
||||
" with open(_OUTPUT_PROTO.value, \"w\") as text_file:\n",
|
||||
" text_file.write(str(model))\n",
|
||||
"\n",
|
||||
" # Solve model.\n",
|
||||
@@ -301,29 +322,29 @@
|
||||
" status = solver.Solve(model)\n",
|
||||
"\n",
|
||||
" # Report solution.\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" used = {i for i in range(num_items) if solver.BooleanValue(is_used[i])}\n",
|
||||
" data = pd.DataFrame({\n",
|
||||
" 'x_start': [solver.Value(x_starts[i]) for i in used],\n",
|
||||
" 'y_start': [solver.Value(y_starts[i]) for i in used],\n",
|
||||
" 'item_width': [item_widths[i] for i in used],\n",
|
||||
" 'item_height': [item_heights[i] for i in used],\n",
|
||||
" 'x_end': [solver.Value(x_starts[i]) + item_widths[i] for i in used],\n",
|
||||
" 'y_end': [\n",
|
||||
" solver.Value(y_starts[i]) + item_heights[i] for i in used\n",
|
||||
" ],\n",
|
||||
" 'item_value': [item_values[i] for i in used]\n",
|
||||
" })\n",
|
||||
" data = pd.DataFrame(\n",
|
||||
" {\n",
|
||||
" \"x_start\": [solver.Value(x_starts[i]) for i in used],\n",
|
||||
" \"y_start\": [solver.Value(y_starts[i]) for i in used],\n",
|
||||
" \"item_width\": [item_widths[i] for i in used],\n",
|
||||
" \"item_height\": [item_heights[i] for i in used],\n",
|
||||
" \"x_end\": [solver.Value(x_starts[i]) + item_widths[i] for i in used],\n",
|
||||
" \"y_end\": [solver.Value(y_starts[i]) + item_heights[i] for i in used],\n",
|
||||
" \"item_value\": [item_values[i] for i in used],\n",
|
||||
" }\n",
|
||||
" )\n",
|
||||
" print(data)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def solve_with_rotations(data, max_height, max_width):\n",
|
||||
" \"\"\"Solve the problem by rotating items.\"\"\"\n",
|
||||
" # Derived data (expanded to individual items).\n",
|
||||
" data_widths = data['width'].to_numpy()\n",
|
||||
" data_heights = data['height'].to_numpy()\n",
|
||||
" data_availability = data['available'].to_numpy()\n",
|
||||
" data_values = data['value'].to_numpy()\n",
|
||||
" data_widths = data[\"width\"].to_numpy()\n",
|
||||
" data_heights = data[\"height\"].to_numpy()\n",
|
||||
" data_availability = data[\"available\"].to_numpy()\n",
|
||||
" data_values = data[\"value\"].to_numpy()\n",
|
||||
"\n",
|
||||
" item_widths = np.repeat(data_widths, data_availability)\n",
|
||||
" item_heights = np.repeat(data_heights, data_availability)\n",
|
||||
@@ -347,26 +368,26 @@
|
||||
" for i in range(num_items):\n",
|
||||
" sizes = [0, int(item_widths[i]), int(item_heights[i])]\n",
|
||||
" # X coordinates.\n",
|
||||
" x_starts.append(model.NewIntVar(0, max_width, f'x_start{i}'))\n",
|
||||
" x_starts.append(model.NewIntVar(0, max_width, f\"x_start{i}\"))\n",
|
||||
" x_sizes.append(\n",
|
||||
" model.NewIntVarFromDomain(cp_model.Domain.FromValues(sizes),\n",
|
||||
" f'x_size{i}'))\n",
|
||||
" x_ends.append(model.NewIntVar(0, max_width, f'x_end{i}'))\n",
|
||||
" model.NewIntVarFromDomain(cp_model.Domain.FromValues(sizes), f\"x_size{i}\")\n",
|
||||
" )\n",
|
||||
" x_ends.append(model.NewIntVar(0, max_width, f\"x_end{i}\"))\n",
|
||||
"\n",
|
||||
" # Y coordinates.\n",
|
||||
" y_starts.append(model.NewIntVar(0, max_height, f'y_start{i}'))\n",
|
||||
" y_starts.append(model.NewIntVar(0, max_height, f\"y_start{i}\"))\n",
|
||||
" y_sizes.append(\n",
|
||||
" model.NewIntVarFromDomain(cp_model.Domain.FromValues(sizes),\n",
|
||||
" f'y_size{i}'))\n",
|
||||
" y_ends.append(model.NewIntVar(0, max_height, f'y_end{i}'))\n",
|
||||
" model.NewIntVarFromDomain(cp_model.Domain.FromValues(sizes), f\"y_size{i}\")\n",
|
||||
" )\n",
|
||||
" y_ends.append(model.NewIntVar(0, max_height, f\"y_end{i}\"))\n",
|
||||
"\n",
|
||||
" ## Interval variables\n",
|
||||
" x_intervals.append(\n",
|
||||
" model.NewIntervalVar(x_starts[i], x_sizes[i], x_ends[i],\n",
|
||||
" f'x_interval{i}'))\n",
|
||||
" model.NewIntervalVar(x_starts[i], x_sizes[i], x_ends[i], f\"x_interval{i}\")\n",
|
||||
" )\n",
|
||||
" y_intervals.append(\n",
|
||||
" model.NewIntervalVar(y_starts[i], y_sizes[i], y_ends[i],\n",
|
||||
" f'y_interval{i}'))\n",
|
||||
" model.NewIntervalVar(y_starts[i], y_sizes[i], y_ends[i], f\"y_interval{i}\")\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # is_used[i] == True if and only if item i is selected.\n",
|
||||
" is_used = []\n",
|
||||
@@ -375,9 +396,9 @@
|
||||
"\n",
|
||||
" ## for each item, decide is unselected, no_rotation, rotated.\n",
|
||||
" for i in range(num_items):\n",
|
||||
" not_selected = model.NewBoolVar(f'not_selected_{i}')\n",
|
||||
" no_rotation = model.NewBoolVar(f'no_rotation_{i}')\n",
|
||||
" rotated = model.NewBoolVar(f'rotated_{i}')\n",
|
||||
" not_selected = model.NewBoolVar(f\"not_selected_{i}\")\n",
|
||||
" no_rotation = model.NewBoolVar(f\"no_rotation_{i}\")\n",
|
||||
" rotated = model.NewBoolVar(f\"rotated_{i}\")\n",
|
||||
"\n",
|
||||
" ### Exactly one state must be chosen.\n",
|
||||
" model.AddExactlyOne(not_selected, no_rotation, rotated)\n",
|
||||
@@ -385,8 +406,12 @@
|
||||
" ### Define height and width according to the state.\n",
|
||||
" dim1 = item_widths[i]\n",
|
||||
" dim2 = item_heights[i]\n",
|
||||
" # Unused boxes are fixed at (0.0).\n",
|
||||
" model.Add(x_sizes[i] == 0).OnlyEnforceIf(not_selected)\n",
|
||||
" model.Add(y_sizes[i] == 0).OnlyEnforceIf(not_selected)\n",
|
||||
" model.Add(x_starts[i] == 0).OnlyEnforceIf(not_selected)\n",
|
||||
" model.Add(y_starts[i] == 0).OnlyEnforceIf(not_selected)\n",
|
||||
" # Sizes are fixed by the rotation.\n",
|
||||
" model.Add(x_sizes[i] == dim1).OnlyEnforceIf(no_rotation)\n",
|
||||
" model.Add(y_sizes[i] == dim2).OnlyEnforceIf(no_rotation)\n",
|
||||
" model.Add(x_sizes[i] == dim2).OnlyEnforceIf(rotated)\n",
|
||||
@@ -402,8 +427,8 @@
|
||||
"\n",
|
||||
" # Output proto to file.\n",
|
||||
" if _OUTPUT_PROTO.value:\n",
|
||||
" print('Writing proto to %s' % _OUTPUT_PROTO.value)\n",
|
||||
" with open(_OUTPUT_PROTO.value, 'w') as text_file:\n",
|
||||
" print(f\"Writing proto to {_OUTPUT_PROTO.value}\")\n",
|
||||
" with open(_OUTPUT_PROTO.value, \"w\") as text_file:\n",
|
||||
" text_file.write(str(model))\n",
|
||||
"\n",
|
||||
" # Solve model.\n",
|
||||
@@ -414,26 +439,28 @@
|
||||
" status = solver.Solve(model)\n",
|
||||
"\n",
|
||||
" # Report solution.\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" used = {i for i in range(num_items) if solver.BooleanValue(is_used[i])}\n",
|
||||
" data = pd.DataFrame({\n",
|
||||
" 'x_start': [solver.Value(x_starts[i]) for i in used],\n",
|
||||
" 'y_start': [solver.Value(y_starts[i]) for i in used],\n",
|
||||
" 'item_width': [solver.Value(x_sizes[i]) for i in used],\n",
|
||||
" 'item_height': [solver.Value(y_sizes[i]) for i in used],\n",
|
||||
" 'x_end': [solver.Value(x_ends[i]) for i in used],\n",
|
||||
" 'y_end': [solver.Value(y_ends[i]) for i in used],\n",
|
||||
" 'item_value': [item_values[i] for i in used]\n",
|
||||
" })\n",
|
||||
" data = pd.DataFrame(\n",
|
||||
" {\n",
|
||||
" \"x_start\": [solver.Value(x_starts[i]) for i in used],\n",
|
||||
" \"y_start\": [solver.Value(y_starts[i]) for i in used],\n",
|
||||
" \"item_width\": [solver.Value(x_sizes[i]) for i in used],\n",
|
||||
" \"item_height\": [solver.Value(y_sizes[i]) for i in used],\n",
|
||||
" \"x_end\": [solver.Value(x_ends[i]) for i in used],\n",
|
||||
" \"y_end\": [solver.Value(y_ends[i]) for i in used],\n",
|
||||
" \"item_value\": [item_values[i] for i in used],\n",
|
||||
" }\n",
|
||||
" )\n",
|
||||
" print(data)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(_):\n",
|
||||
" \"\"\"Solve the problem with all models.\"\"\"\n",
|
||||
" data, max_height, max_width = build_data()\n",
|
||||
" if _MODEL.value == 'duplicate':\n",
|
||||
" if _MODEL.value == \"duplicate\":\n",
|
||||
" solve_with_duplicate_items(data, max_height, max_width)\n",
|
||||
" elif _MODEL.value == 'optional':\n",
|
||||
" elif _MODEL.value == \"optional\":\n",
|
||||
" solve_with_duplicate_optional_items(data, max_height, max_width)\n",
|
||||
" else:\n",
|
||||
" solve_with_rotations(data, max_height, max_width)\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Reader and solver of the single assembly line balancing problem.\n",
|
||||
"\n",
|
||||
"from https://assembly-line-balancing.de/salbp/:\n",
|
||||
@@ -103,12 +104,14 @@
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"_INPUT = flags.DEFINE_string('input', '', 'Input file to parse and solve.')\n",
|
||||
"_PARAMS = flags.DEFINE_string('params', '', 'Sat solver parameters.')\n",
|
||||
"_INPUT = flags.DEFINE_string(\"input\", \"\", \"Input file to parse and solve.\")\n",
|
||||
"_PARAMS = flags.DEFINE_string(\"params\", \"\", \"Sat solver parameters.\")\n",
|
||||
"_OUTPUT_PROTO = flags.DEFINE_string(\n",
|
||||
" 'output_proto', '', 'Output file to write the cp_model proto to.')\n",
|
||||
"_MODEL = flags.DEFINE_string('model', 'boolean',\n",
|
||||
" 'Model used: boolean, scheduling, greedy')\n",
|
||||
" \"output_proto\", \"\", \"Output file to write the cp_model proto to.\"\n",
|
||||
")\n",
|
||||
"_MODEL = flags.DEFINE_string(\n",
|
||||
" \"model\", \"boolean\", \"Model used: boolean, scheduling, greedy\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class SectionInfo(object):\n",
|
||||
@@ -121,13 +124,13 @@
|
||||
"\n",
|
||||
" def __str__(self):\n",
|
||||
" if self.index_map:\n",
|
||||
" return f'SectionInfo(index_map={self.index_map})'\n",
|
||||
" return f\"SectionInfo(index_map={self.index_map})\"\n",
|
||||
" elif self.set_of_pairs:\n",
|
||||
" return f'SectionInfo(set_of_pairs={self.set_of_pairs})'\n",
|
||||
" return f\"SectionInfo(set_of_pairs={self.set_of_pairs})\"\n",
|
||||
" elif self.value is not None:\n",
|
||||
" return f'SectionInfo(value={self.value})'\n",
|
||||
" return f\"SectionInfo(value={self.value})\"\n",
|
||||
" else:\n",
|
||||
" return 'SectionInfo()'\n",
|
||||
" return \"SectionInfo()\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def read_model(filename):\n",
|
||||
@@ -136,64 +139,64 @@
|
||||
" current_info = SectionInfo()\n",
|
||||
"\n",
|
||||
" model = {}\n",
|
||||
" with open(filename, 'r') as input_file:\n",
|
||||
" print(f'Reading model from \\'{filename}\\'')\n",
|
||||
" section_name = ''\n",
|
||||
" with open(filename, \"r\") as input_file:\n",
|
||||
" print(f\"Reading model from '{filename}'\")\n",
|
||||
" section_name = \"\"\n",
|
||||
"\n",
|
||||
" for line in input_file:\n",
|
||||
" stripped_line = line.strip()\n",
|
||||
" if not stripped_line:\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" match_section_def = re.match(r'<([\\w\\s]+)>', stripped_line)\n",
|
||||
" match_section_def = re.match(r\"<([\\w\\s]+)>\", stripped_line)\n",
|
||||
" if match_section_def:\n",
|
||||
" section_name = match_section_def.group(1)\n",
|
||||
" if section_name == 'end':\n",
|
||||
" if section_name == \"end\":\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" current_info = SectionInfo()\n",
|
||||
" model[section_name] = current_info\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" match_single_number = re.match(r'^([0-9]+)$', stripped_line)\n",
|
||||
" match_single_number = re.match(r\"^([0-9]+)$\", stripped_line)\n",
|
||||
" if match_single_number:\n",
|
||||
" current_info.value = int(match_single_number.group(1))\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" match_key_value = re.match(r'^([0-9]+)\\s+([0-9]+)$', stripped_line)\n",
|
||||
" match_key_value = re.match(r\"^([0-9]+)\\s+([0-9]+)$\", stripped_line)\n",
|
||||
" if match_key_value:\n",
|
||||
" key = int(match_key_value.group(1))\n",
|
||||
" value = int(match_key_value.group(2))\n",
|
||||
" current_info.index_map[key] = value\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" match_pair = re.match(r'^([0-9]+),([0-9]+)$', stripped_line)\n",
|
||||
" match_pair = re.match(r\"^([0-9]+),([0-9]+)$\", stripped_line)\n",
|
||||
" if match_pair:\n",
|
||||
" left = int(match_pair.group(1))\n",
|
||||
" right = int(match_pair.group(2))\n",
|
||||
" current_info.set_of_pairs.add((left, right))\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" print(f'Unrecognized line \\'{stripped_line}\\'')\n",
|
||||
" print(f\"Unrecognized line '{stripped_line}'\")\n",
|
||||
"\n",
|
||||
" return model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_stats(model):\n",
|
||||
" print('Model Statistics')\n",
|
||||
" print(\"Model Statistics\")\n",
|
||||
" for key, value in model.items():\n",
|
||||
" print(f' - {key}: {value}')\n",
|
||||
" print(f\" - {key}: {value}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def solve_model_greedily(model):\n",
|
||||
" \"\"\"Compute a greedy solution.\"\"\"\n",
|
||||
" print('Solving using a Greedy heuristics')\n",
|
||||
" print(\"Solving using a Greedy heuristics\")\n",
|
||||
"\n",
|
||||
" num_tasks = model['number of tasks'].value\n",
|
||||
" num_tasks = model[\"number of tasks\"].value\n",
|
||||
" all_tasks = range(1, num_tasks + 1) # Tasks are 1 based in the data.\n",
|
||||
" precedences = model['precedence relations'].set_of_pairs\n",
|
||||
" durations = model['task times'].index_map\n",
|
||||
" cycle_time = model['cycle time'].value\n",
|
||||
" precedences = model[\"precedence relations\"].set_of_pairs\n",
|
||||
" durations = model[\"task times\"].index_map\n",
|
||||
" cycle_time = model[\"cycle time\"].value\n",
|
||||
"\n",
|
||||
" weights = collections.defaultdict(int)\n",
|
||||
" successors = collections.defaultdict(list)\n",
|
||||
@@ -212,7 +215,7 @@
|
||||
"\n",
|
||||
" while len(assignment) < num_tasks:\n",
|
||||
" if not candidates:\n",
|
||||
" print('error empty')\n",
|
||||
" print(\"error empty\")\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
" best = -1\n",
|
||||
@@ -242,7 +245,7 @@
|
||||
" candidates.add(succ)\n",
|
||||
" del weights[succ]\n",
|
||||
"\n",
|
||||
" print(f' greedy solution uses {current_pod + 1} pods.')\n",
|
||||
" print(f\" greedy solution uses {current_pod + 1} pods.\")\n",
|
||||
"\n",
|
||||
" return assignment\n",
|
||||
"\n",
|
||||
@@ -250,13 +253,13 @@
|
||||
"def solve_boolean_model(model, hint):\n",
|
||||
" \"\"\"Solve the given model.\"\"\"\n",
|
||||
"\n",
|
||||
" print('Solving using the Boolean model')\n",
|
||||
" print(\"Solving using the Boolean model\")\n",
|
||||
" # Model data\n",
|
||||
" num_tasks = model['number of tasks'].value\n",
|
||||
" num_tasks = model[\"number of tasks\"].value\n",
|
||||
" all_tasks = range(1, num_tasks + 1) # Tasks are 1 based in the model.\n",
|
||||
" durations = model['task times'].index_map\n",
|
||||
" precedences = model['precedence relations'].set_of_pairs\n",
|
||||
" cycle_time = model['cycle time'].value\n",
|
||||
" durations = model[\"task times\"].index_map\n",
|
||||
" precedences = model[\"precedence relations\"].set_of_pairs\n",
|
||||
" cycle_time = model[\"cycle time\"].value\n",
|
||||
"\n",
|
||||
" num_pods = max(p for _, p in hint.items()) + 1 if hint else num_tasks - 1\n",
|
||||
" all_pods = range(num_pods)\n",
|
||||
@@ -271,11 +274,11 @@
|
||||
" # Create the variables\n",
|
||||
" for t in all_tasks:\n",
|
||||
" for p in all_pods:\n",
|
||||
" assign[t, p] = model.NewBoolVar(f'assign_{t}_{p}')\n",
|
||||
" possible[t, p] = model.NewBoolVar(f'possible_{t}_{p}')\n",
|
||||
" assign[t, p] = model.NewBoolVar(f\"assign_{t}_{p}\")\n",
|
||||
" possible[t, p] = model.NewBoolVar(f\"possible_{t}_{p}\")\n",
|
||||
"\n",
|
||||
" # active[p] indicates if pod p is active.\n",
|
||||
" active = [model.NewBoolVar(f'active_{p}') for p in all_pods]\n",
|
||||
" active = [model.NewBoolVar(f\"active_{p}\") for p in all_pods]\n",
|
||||
"\n",
|
||||
" # Each task is done on exactly one pod.\n",
|
||||
" for t in all_tasks:\n",
|
||||
@@ -283,8 +286,7 @@
|
||||
"\n",
|
||||
" # Total tasks assigned to one pod cannot exceed cycle time.\n",
|
||||
" for p in all_pods:\n",
|
||||
" model.Add(\n",
|
||||
" sum(assign[t, p] * durations[t] for t in all_tasks) <= cycle_time)\n",
|
||||
" model.Add(sum(assign[t, p] * durations[t] for t in all_tasks) <= cycle_time)\n",
|
||||
"\n",
|
||||
" # Maintain the possible variables:\n",
|
||||
" # possible at pod p -> possible at any pod after p\n",
|
||||
@@ -302,8 +304,7 @@
|
||||
" # Precedences.\n",
|
||||
" for before, after in precedences:\n",
|
||||
" for p in range(1, num_pods):\n",
|
||||
" model.AddImplication(assign[before, p], possible[after,\n",
|
||||
" p - 1].Not())\n",
|
||||
" model.AddImplication(assign[before, p], possible[after, p - 1].Not())\n",
|
||||
"\n",
|
||||
" # Link active variables with the assign one.\n",
|
||||
" for p in all_pods:\n",
|
||||
@@ -327,7 +328,7 @@
|
||||
" model.AddHint(assign[t, hint[t]], 1)\n",
|
||||
"\n",
|
||||
" if _OUTPUT_PROTO.value:\n",
|
||||
" print(f'Writing proto to {_OUTPUT_PROTO.value}')\n",
|
||||
" print(f\"Writing proto to {_OUTPUT_PROTO.value}\")\n",
|
||||
" model.ExportToFile(_OUTPUT_PROTO.value)\n",
|
||||
"\n",
|
||||
" # Solve model.\n",
|
||||
@@ -341,13 +342,13 @@
|
||||
"def solve_scheduling_model(model, hint):\n",
|
||||
" \"\"\"Solve the given model using a cumutive model.\"\"\"\n",
|
||||
"\n",
|
||||
" print('Solving using the scheduling model')\n",
|
||||
" print(\"Solving using the scheduling model\")\n",
|
||||
" # Model data\n",
|
||||
" num_tasks = model['number of tasks'].value\n",
|
||||
" num_tasks = model[\"number of tasks\"].value\n",
|
||||
" all_tasks = range(1, num_tasks + 1) # Tasks are 1 based in the data.\n",
|
||||
" durations = model['task times'].index_map\n",
|
||||
" precedences = model['precedence relations'].set_of_pairs\n",
|
||||
" cycle_time = model['cycle time'].value\n",
|
||||
" durations = model[\"task times\"].index_map\n",
|
||||
" precedences = model[\"precedence relations\"].set_of_pairs\n",
|
||||
" cycle_time = model[\"cycle time\"].value\n",
|
||||
"\n",
|
||||
" num_pods = max(p for _, p in hint.items()) + 1 if hint else num_tasks\n",
|
||||
"\n",
|
||||
@@ -356,21 +357,20 @@
|
||||
" # pod[t] indicates on which pod the task is performed.\n",
|
||||
" pods = {}\n",
|
||||
" for t in all_tasks:\n",
|
||||
" pods[t] = model.NewIntVar(0, num_pods - 1, f'pod_{t}')\n",
|
||||
" pods[t] = model.NewIntVar(0, num_pods - 1, f\"pod_{t}\")\n",
|
||||
"\n",
|
||||
" # Create the variables\n",
|
||||
" intervals = []\n",
|
||||
" demands = []\n",
|
||||
" for t in all_tasks:\n",
|
||||
" interval = model.NewFixedSizeIntervalVar(pods[t], 1, '')\n",
|
||||
" interval = model.NewFixedSizeIntervalVar(pods[t], 1, \"\")\n",
|
||||
" intervals.append(interval)\n",
|
||||
" demands.append(durations[t])\n",
|
||||
"\n",
|
||||
" # Add terminating interval as the objective.\n",
|
||||
" obj_var = model.NewIntVar(1, num_pods, 'obj_var')\n",
|
||||
" obj_size = model.NewIntVar(1, num_pods, 'obj_duration')\n",
|
||||
" obj_interval = model.NewIntervalVar(obj_var, obj_size, num_pods + 1,\n",
|
||||
" 'obj_interval')\n",
|
||||
" obj_var = model.NewIntVar(1, num_pods, \"obj_var\")\n",
|
||||
" obj_size = model.NewIntVar(1, num_pods, \"obj_duration\")\n",
|
||||
" obj_interval = model.NewIntervalVar(obj_var, obj_size, num_pods + 1, \"obj_interval\")\n",
|
||||
" intervals.append(obj_interval)\n",
|
||||
" demands.append(cycle_time)\n",
|
||||
"\n",
|
||||
@@ -389,7 +389,7 @@
|
||||
" model.AddHint(pods[t], hint[t])\n",
|
||||
"\n",
|
||||
" if _OUTPUT_PROTO.value:\n",
|
||||
" print(f'Writing proto to{_OUTPUT_PROTO.value}')\n",
|
||||
" print(f\"Writing proto to{_OUTPUT_PROTO.value}\")\n",
|
||||
" model.ExportToFile(_OUTPUT_PROTO.value)\n",
|
||||
"\n",
|
||||
" # Solve model.\n",
|
||||
@@ -402,15 +402,15 @@
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
"\n",
|
||||
" model = read_model(_INPUT.value)\n",
|
||||
" print_stats(model)\n",
|
||||
" greedy_solution = solve_model_greedily(model)\n",
|
||||
"\n",
|
||||
" if _MODEL.value == 'boolean':\n",
|
||||
" if _MODEL.value == \"boolean\":\n",
|
||||
" solve_boolean_model(model, greedy_solution)\n",
|
||||
" elif _MODEL.value == 'scheduling':\n",
|
||||
" elif _MODEL.value == \"scheduling\":\n",
|
||||
" solve_scheduling_model(model, greedy_solution)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Test linear sum assignment on a 4x4 matrix.\n",
|
||||
"\n",
|
||||
" Example taken from:\n",
|
||||
@@ -95,8 +96,7 @@
|
||||
" \"\"\"Test linear sum assignment on a 4x4 matrix.\"\"\"\n",
|
||||
" num_sources = 4\n",
|
||||
" num_targets = 4\n",
|
||||
" cost = [[90, 76, 75, 80], [35, 85, 55, 65], [125, 95, 90, 105],\n",
|
||||
" [45, 110, 95, 115]]\n",
|
||||
" cost = [[90, 76, 75, 80], [35, 85, 55, 65], [125, 95, 90, 105], [45, 110, 95, 115]]\n",
|
||||
" expected_cost = cost[0][3] + cost[1][2] + cost[2][1] + cost[3][0]\n",
|
||||
"\n",
|
||||
" assignment = linear_sum_assignment.SimpleLinearSumAssignment()\n",
|
||||
@@ -106,21 +106,22 @@
|
||||
"\n",
|
||||
" solve_status = assignment.solve()\n",
|
||||
" if solve_status == assignment.OPTIMAL:\n",
|
||||
" print('Successful solve.')\n",
|
||||
" print('Total cost', assignment.optimal_cost(), '/', expected_cost)\n",
|
||||
" print(\"Successful solve.\")\n",
|
||||
" print(\"Total cost\", assignment.optimal_cost(), \"/\", expected_cost)\n",
|
||||
" for i in range(0, assignment.num_nodes()):\n",
|
||||
" print('Left node %d assigned to right node %d with cost %d.' %\n",
|
||||
" (i, assignment.right_mate(i), assignment.assignment_cost(i)))\n",
|
||||
" print(\n",
|
||||
" \"Left node %d assigned to right node %d with cost %d.\"\n",
|
||||
" % (i, assignment.right_mate(i), assignment.assignment_cost(i))\n",
|
||||
" )\n",
|
||||
" elif solve_status == assignment.INFEASIBLE:\n",
|
||||
" print('No perfect matching exists.')\n",
|
||||
" print(\"No perfect matching exists.\")\n",
|
||||
" elif solve_status == assignment.POSSIBLE_OVERFLOW:\n",
|
||||
" print(\n",
|
||||
" 'Some input costs are too large and may cause an integer overflow.')\n",
|
||||
" print(\"Some input costs are too large and may cause an integer overflow.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" run_assignment_on_4x4_matrix()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Linear programming examples that show how to use the APIs.\n"
|
||||
]
|
||||
},
|
||||
@@ -86,8 +87,9 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"def Announce(solver, api_type):\n",
|
||||
" print('---- Linear programming example with ' + solver + ' (' + api_type +\n",
|
||||
" ') -----')\n",
|
||||
" print(\n",
|
||||
" \"---- Linear programming example with \" + solver + \" (\" + api_type + \") -----\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def RunLinearExampleNaturalLanguageAPI(optimization_problem_type):\n",
|
||||
@@ -97,23 +99,25 @@
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" Announce(optimization_problem_type, 'natural language API')\n",
|
||||
" Announce(optimization_problem_type, \"natural language API\")\n",
|
||||
"\n",
|
||||
" infinity = solver.infinity()\n",
|
||||
" # x1, x2 and x3 are continuous non-negative variables.\n",
|
||||
" x1 = solver.NumVar(0.0, infinity, 'x1')\n",
|
||||
" x2 = solver.NumVar(0.0, infinity, 'x2')\n",
|
||||
" x3 = solver.NumVar(0.0, infinity, 'x3')\n",
|
||||
" x1 = solver.NumVar(0.0, infinity, \"x1\")\n",
|
||||
" x2 = solver.NumVar(0.0, infinity, \"x2\")\n",
|
||||
" x3 = solver.NumVar(0.0, infinity, \"x3\")\n",
|
||||
"\n",
|
||||
" solver.Maximize(10 * x1 + 6 * x2 + 4 * x3)\n",
|
||||
" c0 = solver.Add(10 * x1 + 4 * x2 + 5 * x3 <= 600, 'ConstraintName0')\n",
|
||||
" c0 = solver.Add(10 * x1 + 4 * x2 + 5 * x3 <= 600, \"ConstraintName0\")\n",
|
||||
" c1 = solver.Add(2 * x1 + 2 * x2 + 6 * x3 <= 300)\n",
|
||||
" sum_of_vars = sum([x1, x2, x3])\n",
|
||||
" c2 = solver.Add(sum_of_vars <= 100.0, 'OtherConstraintName')\n",
|
||||
" c2 = solver.Add(sum_of_vars <= 100.0, \"OtherConstraintName\")\n",
|
||||
"\n",
|
||||
" SolveAndPrint(solver, [x1, x2, x3], [c0, c1, c2], optimization_problem_type != 'PDLP')\n",
|
||||
" SolveAndPrint(\n",
|
||||
" solver, [x1, x2, x3], [c0, c1, c2], optimization_problem_type != \"PDLP\"\n",
|
||||
" )\n",
|
||||
" # Print a linear expression's solution value.\n",
|
||||
" print('Sum of vars: %s = %s' % (sum_of_vars, sum_of_vars.solution_value()))\n",
|
||||
" print(\"Sum of vars: %s = %s\" % (sum_of_vars, sum_of_vars.solution_value()))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def RunLinearExampleCppStyleAPI(optimization_problem_type):\n",
|
||||
@@ -122,13 +126,13 @@
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" Announce(optimization_problem_type, 'C++ style API')\n",
|
||||
" Announce(optimization_problem_type, \"C++ style API\")\n",
|
||||
"\n",
|
||||
" infinity = solver.infinity()\n",
|
||||
" # x1, x2 and x3 are continuous non-negative variables.\n",
|
||||
" x1 = solver.NumVar(0.0, infinity, 'x1')\n",
|
||||
" x2 = solver.NumVar(0.0, infinity, 'x2')\n",
|
||||
" x3 = solver.NumVar(0.0, infinity, 'x3')\n",
|
||||
" x1 = solver.NumVar(0.0, infinity, \"x1\")\n",
|
||||
" x2 = solver.NumVar(0.0, infinity, \"x2\")\n",
|
||||
" x3 = solver.NumVar(0.0, infinity, \"x3\")\n",
|
||||
"\n",
|
||||
" # Maximize 10 * x1 + 6 * x2 + 4 * x3.\n",
|
||||
" objective = solver.Objective()\n",
|
||||
@@ -138,31 +142,32 @@
|
||||
" objective.SetMaximization()\n",
|
||||
"\n",
|
||||
" # x1 + x2 + x3 <= 100.\n",
|
||||
" c0 = solver.Constraint(-infinity, 100.0, 'c0')\n",
|
||||
" c0 = solver.Constraint(-infinity, 100.0, \"c0\")\n",
|
||||
" c0.SetCoefficient(x1, 1)\n",
|
||||
" c0.SetCoefficient(x2, 1)\n",
|
||||
" c0.SetCoefficient(x3, 1)\n",
|
||||
"\n",
|
||||
" # 10 * x1 + 4 * x2 + 5 * x3 <= 600.\n",
|
||||
" c1 = solver.Constraint(-infinity, 600.0, 'c1')\n",
|
||||
" c1 = solver.Constraint(-infinity, 600.0, \"c1\")\n",
|
||||
" c1.SetCoefficient(x1, 10)\n",
|
||||
" c1.SetCoefficient(x2, 4)\n",
|
||||
" c1.SetCoefficient(x3, 5)\n",
|
||||
"\n",
|
||||
" # 2 * x1 + 2 * x2 + 6 * x3 <= 300.\n",
|
||||
" c2 = solver.Constraint(-infinity, 300.0, 'c2')\n",
|
||||
" c2 = solver.Constraint(-infinity, 300.0, \"c2\")\n",
|
||||
" c2.SetCoefficient(x1, 2)\n",
|
||||
" c2.SetCoefficient(x2, 2)\n",
|
||||
" c2.SetCoefficient(x3, 6)\n",
|
||||
"\n",
|
||||
" SolveAndPrint(solver, [x1, x2, x3], [c0, c1, c2],\n",
|
||||
" optimization_problem_type != 'PDLP')\n",
|
||||
" SolveAndPrint(\n",
|
||||
" solver, [x1, x2, x3], [c0, c1, c2], optimization_problem_type != \"PDLP\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def SolveAndPrint(solver, variable_list, constraint_list, is_precise):\n",
|
||||
" \"\"\"Solve the problem and print the solution.\"\"\"\n",
|
||||
" print('Number of variables = %d' % solver.NumVariables())\n",
|
||||
" print('Number of constraints = %d' % solver.NumConstraints())\n",
|
||||
" print(\"Number of variables = %d\" % solver.NumVariables())\n",
|
||||
" print(\"Number of constraints = %d\" % solver.NumConstraints())\n",
|
||||
"\n",
|
||||
" result_status = solver.Solve()\n",
|
||||
"\n",
|
||||
@@ -174,37 +179,39 @@
|
||||
" if is_precise:\n",
|
||||
" assert solver.VerifySolution(1e-7, True)\n",
|
||||
"\n",
|
||||
" print('Problem solved in %f milliseconds' % solver.wall_time())\n",
|
||||
" print(\"Problem solved in %f milliseconds\" % solver.wall_time())\n",
|
||||
"\n",
|
||||
" # The objective value of the solution.\n",
|
||||
" print('Optimal objective value = %f' % solver.Objective().Value())\n",
|
||||
" print(\"Optimal objective value = %f\" % solver.Objective().Value())\n",
|
||||
"\n",
|
||||
" # The value of each variable in the solution.\n",
|
||||
" for variable in variable_list:\n",
|
||||
" print('%s = %f' % (variable.name(), variable.solution_value()))\n",
|
||||
" print(\"%s = %f\" % (variable.name(), variable.solution_value()))\n",
|
||||
"\n",
|
||||
" print('Advanced usage:')\n",
|
||||
" print('Problem solved in %d iterations' % solver.iterations())\n",
|
||||
" print(\"Advanced usage:\")\n",
|
||||
" print(\"Problem solved in %d iterations\" % solver.iterations())\n",
|
||||
" for variable in variable_list:\n",
|
||||
" print('%s: reduced cost = %f' %\n",
|
||||
" (variable.name(), variable.reduced_cost()))\n",
|
||||
" print(\"%s: reduced cost = %f\" % (variable.name(), variable.reduced_cost()))\n",
|
||||
" activities = solver.ComputeConstraintActivities()\n",
|
||||
" for i, constraint in enumerate(constraint_list):\n",
|
||||
" print(('constraint %d: dual value = %f\\n'\n",
|
||||
" ' activity = %f' %\n",
|
||||
" (i, constraint.dual_value(), activities[constraint.index()])))\n",
|
||||
" print(\n",
|
||||
" (\n",
|
||||
" \"constraint %d: dual value = %f\\n activity = %f\"\n",
|
||||
" % (i, constraint.dual_value(), activities[constraint.index()])\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" RunLinearExampleNaturalLanguageAPI('GLOP')\n",
|
||||
" RunLinearExampleNaturalLanguageAPI('GLPK_LP')\n",
|
||||
" RunLinearExampleNaturalLanguageAPI('CLP')\n",
|
||||
" RunLinearExampleNaturalLanguageAPI('PDLP')\n",
|
||||
" RunLinearExampleNaturalLanguageAPI(\"GLOP\")\n",
|
||||
" RunLinearExampleNaturalLanguageAPI(\"GLPK_LP\")\n",
|
||||
" RunLinearExampleNaturalLanguageAPI(\"CLP\")\n",
|
||||
" RunLinearExampleNaturalLanguageAPI(\"PDLP\")\n",
|
||||
"\n",
|
||||
" RunLinearExampleCppStyleAPI('GLOP')\n",
|
||||
" RunLinearExampleCppStyleAPI('GLPK_LP')\n",
|
||||
" RunLinearExampleCppStyleAPI('CLP')\n",
|
||||
" RunLinearExampleCppStyleAPI('PDLP')\n",
|
||||
" RunLinearExampleCppStyleAPI(\"GLOP\")\n",
|
||||
" RunLinearExampleCppStyleAPI(\"GLPK_LP\")\n",
|
||||
" RunLinearExampleCppStyleAPI(\"CLP\")\n",
|
||||
" RunLinearExampleCppStyleAPI(\"PDLP\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Magic sequence problem.\n",
|
||||
"\n",
|
||||
"This models aims at building a sequence of numbers such that the number of\n",
|
||||
@@ -96,12 +97,12 @@
|
||||
"\n",
|
||||
"def main(argv):\n",
|
||||
" # Create the solver.\n",
|
||||
" solver = pywrapcp.Solver('magic sequence')\n",
|
||||
" solver = pywrapcp.Solver(\"magic sequence\")\n",
|
||||
"\n",
|
||||
" # Create an array of IntVars to hold the answers.\n",
|
||||
" size = int(argv[1]) if len(argv) > 1 else 100\n",
|
||||
" all_values = list(range(0, size))\n",
|
||||
" all_vars = [solver.IntVar(0, size, 'vars_%d' % i) for i in all_values]\n",
|
||||
" all_vars = [solver.IntVar(0, size, \"vars_%d\" % i) for i in all_values]\n",
|
||||
"\n",
|
||||
" # The number of variables equal to j shall be the value of all_vars[j].\n",
|
||||
" solver.Add(solver.Distribute(all_vars, all_values, all_vars))\n",
|
||||
@@ -111,8 +112,8 @@
|
||||
" solver.Add(solver.Sum(all_vars) == size)\n",
|
||||
"\n",
|
||||
" solver.NewSearch(\n",
|
||||
" solver.Phase(all_vars, solver.CHOOSE_FIRST_UNBOUND,\n",
|
||||
" solver.ASSIGN_MIN_VALUE))\n",
|
||||
" solver.Phase(all_vars, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE)\n",
|
||||
" )\n",
|
||||
" solver.NextSolution()\n",
|
||||
" print(all_vars)\n",
|
||||
" solver.EndSearch()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Excape the maze while collecting treasures in order.\n",
|
||||
"\n",
|
||||
"The path must begin at the 'start' position, finish at the 'end' position,\n",
|
||||
@@ -94,23 +95,29 @@
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"_OUTPUT_PROTO = flags.DEFINE_string(\n",
|
||||
" 'output_proto', '', 'Output file to write the cp_model proto to.')\n",
|
||||
"_PARAMS = flags.DEFINE_string('params',\n",
|
||||
" 'num_search_workers:8,log_search_progress:true',\n",
|
||||
" 'Sat solver parameters.')\n",
|
||||
" \"output_proto\", \"\", \"Output file to write the cp_model proto to.\"\n",
|
||||
")\n",
|
||||
"_PARAMS = flags.DEFINE_string(\n",
|
||||
" \"params\", \"num_search_workers:8,log_search_progress:true\", \"Sat solver parameters.\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def add_neighbor(size, x, y, z, dx, dy, dz, model, index_map, position_to_rank,\n",
|
||||
" arcs):\n",
|
||||
"def add_neighbor(size, x, y, z, dx, dy, dz, model, index_map, position_to_rank, arcs):\n",
|
||||
" \"\"\"Checks if the neighbor is valid, and adds it to the model.\"\"\"\n",
|
||||
" if (x + dx < 0 or x + dx >= size or y + dy < 0 or y + dy >= size or\n",
|
||||
" z + dz < 0 or z + dz >= size):\n",
|
||||
" if (\n",
|
||||
" x + dx < 0\n",
|
||||
" or x + dx >= size\n",
|
||||
" or y + dy < 0\n",
|
||||
" or y + dy >= size\n",
|
||||
" or z + dz < 0\n",
|
||||
" or z + dz >= size\n",
|
||||
" ):\n",
|
||||
" return\n",
|
||||
" before_index = index_map[(x, y, z)]\n",
|
||||
" before_rank = position_to_rank[(x, y, z)]\n",
|
||||
" after_index = index_map[(x + dx, y + dy, z + dz)]\n",
|
||||
" after_rank = position_to_rank[(x + dx, y + dy, z + dz)]\n",
|
||||
" move_literal = model.NewBoolVar('')\n",
|
||||
" move_literal = model.NewBoolVar(\"\")\n",
|
||||
" model.Add(after_rank == before_rank + 1).OnlyEnforceIf(move_literal)\n",
|
||||
" arcs.append((before_index, after_index, move_literal))\n",
|
||||
"\n",
|
||||
@@ -139,8 +146,7 @@
|
||||
" position_to_rank = {}\n",
|
||||
"\n",
|
||||
" for coord in reverse_map:\n",
|
||||
" position_to_rank[coord] = model.NewIntVar(0, counter - 1,\n",
|
||||
" f'rank_{coord}')\n",
|
||||
" position_to_rank[coord] = model.NewIntVar(0, counter - 1, f\"rank_{coord}\")\n",
|
||||
"\n",
|
||||
" # Path constraints.\n",
|
||||
" model.Add(position_to_rank[start] == 0)\n",
|
||||
@@ -154,18 +160,24 @@
|
||||
" for x in range(size):\n",
|
||||
" for y in range(size):\n",
|
||||
" for z in range(size):\n",
|
||||
" add_neighbor(size, x, y, z, -1, 0, 0, model, index_map,\n",
|
||||
" position_to_rank, arcs)\n",
|
||||
" add_neighbor(size, x, y, z, 1, 0, 0, model, index_map,\n",
|
||||
" position_to_rank, arcs)\n",
|
||||
" add_neighbor(size, x, y, z, 0, -1, 0, model, index_map,\n",
|
||||
" position_to_rank, arcs)\n",
|
||||
" add_neighbor(size, x, y, z, 0, 1, 0, model, index_map,\n",
|
||||
" position_to_rank, arcs)\n",
|
||||
" add_neighbor(size, x, y, z, 0, 0, -1, model, index_map,\n",
|
||||
" position_to_rank, arcs)\n",
|
||||
" add_neighbor(size, x, y, z, 0, 0, 1, model, index_map,\n",
|
||||
" position_to_rank, arcs)\n",
|
||||
" add_neighbor(\n",
|
||||
" size, x, y, z, -1, 0, 0, model, index_map, position_to_rank, arcs\n",
|
||||
" )\n",
|
||||
" add_neighbor(\n",
|
||||
" size, x, y, z, 1, 0, 0, model, index_map, position_to_rank, arcs\n",
|
||||
" )\n",
|
||||
" add_neighbor(\n",
|
||||
" size, x, y, z, 0, -1, 0, model, index_map, position_to_rank, arcs\n",
|
||||
" )\n",
|
||||
" add_neighbor(\n",
|
||||
" size, x, y, z, 0, 1, 0, model, index_map, position_to_rank, arcs\n",
|
||||
" )\n",
|
||||
" add_neighbor(\n",
|
||||
" size, x, y, z, 0, 0, -1, model, index_map, position_to_rank, arcs\n",
|
||||
" )\n",
|
||||
" add_neighbor(\n",
|
||||
" size, x, y, z, 0, 0, 1, model, index_map, position_to_rank, arcs\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Closes the loop as the constraint expects a circuit, not a path.\n",
|
||||
" arcs.append((index_map[end], index_map[start], True))\n",
|
||||
@@ -186,28 +198,28 @@
|
||||
"\n",
|
||||
" # Prints solution.\n",
|
||||
" if result == cp_model.OPTIMAL:\n",
|
||||
" path = [''] * counter\n",
|
||||
" path = [\"\"] * counter\n",
|
||||
" for x in range(size):\n",
|
||||
" for y in range(size):\n",
|
||||
" for z in range(size):\n",
|
||||
" position = (x, y, z)\n",
|
||||
" rank = solver.Value(position_to_rank[position])\n",
|
||||
" msg = f'({x}, {y}, {z})'\n",
|
||||
" msg = f\"({x}, {y}, {z})\"\n",
|
||||
" if position == start:\n",
|
||||
" msg += ' [start]'\n",
|
||||
" msg += \" [start]\"\n",
|
||||
" elif position == end:\n",
|
||||
" msg += ' [end]'\n",
|
||||
" msg += \" [end]\"\n",
|
||||
" else:\n",
|
||||
" for b in range(len(boxes)):\n",
|
||||
" if position == boxes[b]:\n",
|
||||
" msg += f' [boxes {b}]'\n",
|
||||
" msg += f\" [boxes {b}]\"\n",
|
||||
" path[rank] = msg\n",
|
||||
" print(path)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" escape_the_maze(_PARAMS.value, _OUTPUT_PROTO.value)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Scheduling cooking tasks in a bakery using no-wait jobshop scheduling.\n",
|
||||
"\n",
|
||||
"We are scheduling a full day of baking:\n",
|
||||
@@ -93,34 +94,35 @@
|
||||
"from google.protobuf import text_format\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"_PARAMS = flags.DEFINE_string('params',\n",
|
||||
" 'num_search_workers:16, max_time_in_seconds:30',\n",
|
||||
" 'Sat solver parameters.')\n",
|
||||
"_PARAMS = flags.DEFINE_string(\n",
|
||||
" \"params\", \"num_search_workers:16, max_time_in_seconds:30\", \"Sat solver parameters.\"\n",
|
||||
")\n",
|
||||
"_PROTO_FILE = flags.DEFINE_string(\n",
|
||||
" 'proto_file', '', 'If not empty, output the proto to this file.')\n",
|
||||
" \"proto_file\", \"\", \"If not empty, output the proto to this file.\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Recipes\n",
|
||||
"CROISSANT = 'croissant'\n",
|
||||
"APPLE_PIE = 'apple pie'\n",
|
||||
"BRIOCHE = 'brioche'\n",
|
||||
"CHOCOLATE_CAKE = 'chocolate cake'\n",
|
||||
"CROISSANT = \"croissant\"\n",
|
||||
"APPLE_PIE = \"apple pie\"\n",
|
||||
"BRIOCHE = \"brioche\"\n",
|
||||
"CHOCOLATE_CAKE = \"chocolate cake\"\n",
|
||||
"\n",
|
||||
"# Skills\n",
|
||||
"BAKING = 'baking'\n",
|
||||
"PROOFING = 'proofing'\n",
|
||||
"COOKING = 'cooking'\n",
|
||||
"COOLING = 'cooling'\n",
|
||||
"DECORATING = 'decorating'\n",
|
||||
"DISPLAY = 'display'\n",
|
||||
"BAKING = \"baking\"\n",
|
||||
"PROOFING = \"proofing\"\n",
|
||||
"COOKING = \"cooking\"\n",
|
||||
"COOLING = \"cooling\"\n",
|
||||
"DECORATING = \"decorating\"\n",
|
||||
"DISPLAY = \"display\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Task(object):\n",
|
||||
" \"\"\"A unit baking task.\n",
|
||||
"\n",
|
||||
" - Simple baking tasks have a fixed duration. They are performed by workers.\n",
|
||||
" - Waiting/cooling/proofing tasks have a min and a max duration.\n",
|
||||
" They are performed by machine or they use space resources.\n",
|
||||
" \"\"\"\n",
|
||||
" - Simple baking tasks have a fixed duration. They are performed by workers.\n",
|
||||
" - Waiting/cooling/proofing tasks have a min and a max duration.\n",
|
||||
" They are performed by machine or they use space resources.\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" def __init__(self, name, min_duration, max_duration):\n",
|
||||
" self.name = name\n",
|
||||
@@ -152,13 +154,13 @@
|
||||
"class Resource(object):\n",
|
||||
" \"\"\"A resource is a worker, a machine, or just some space for cakes to rest.\n",
|
||||
"\n",
|
||||
" - Workers have a capacity of 1 and can have variable efficiency.\n",
|
||||
" - Machines and spaces have a capacity greater or equal to one, but the\n",
|
||||
" efficiency is fixed to 100.\n",
|
||||
" - Workers have a capacity of 1 and can have variable efficiency.\n",
|
||||
" - Machines and spaces have a capacity greater or equal to one, but the\n",
|
||||
" efficiency is fixed to 100.\n",
|
||||
"\n",
|
||||
" For a worker with efficiency k and a task of duration t, the resulting\n",
|
||||
" work will have a duration `ceil(t * k)`.\n",
|
||||
" \"\"\"\n",
|
||||
" For a worker with efficiency k and a task of duration t, the resulting\n",
|
||||
" work will have a duration `ceil(t * k)`.\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" def __init__(self, name, capacity):\n",
|
||||
" self.name = name\n",
|
||||
@@ -176,12 +178,12 @@
|
||||
" def __init__(self, unique_id, recipe_name, due_date, quantity):\n",
|
||||
" \"\"\"Builds an order.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" unique_id: A unique identifier for the order. Used to display the result.\n",
|
||||
" recipe_name: The name of the recipe. It must match one of the recipes.\n",
|
||||
" due_date: The due date in minutes since midnight.\n",
|
||||
" quantity: How many cakes to prepare.\n",
|
||||
" \"\"\"\n",
|
||||
" Args:\n",
|
||||
" unique_id: A unique identifier for the order. Used to display the result.\n",
|
||||
" recipe_name: The name of the recipe. It must match one of the recipes.\n",
|
||||
" due_date: The due date in minutes since midnight.\n",
|
||||
" quantity: How many cakes to prepare.\n",
|
||||
" \"\"\"\n",
|
||||
" self.unique_id = unique_id\n",
|
||||
" self.recipe_name = recipe_name\n",
|
||||
" self.due_date = due_date\n",
|
||||
@@ -217,32 +219,41 @@
|
||||
" chocolate_cake_recipe.add_task(DECORATING, 15, 15)\n",
|
||||
" chocolate_cake_recipe.add_task(DISPLAY, 5, 5 * 60)\n",
|
||||
" recipes = [\n",
|
||||
" croissant_recipe, apple_pie_recipe, brioche_recipe,\n",
|
||||
" chocolate_cake_recipe\n",
|
||||
" croissant_recipe,\n",
|
||||
" apple_pie_recipe,\n",
|
||||
" brioche_recipe,\n",
|
||||
" chocolate_cake_recipe,\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" # Resources.\n",
|
||||
" baker1 = Resource('baker1', 1).add_skill(BAKING, 1.0)\n",
|
||||
" baker2 = Resource('baker2', 1).add_skill(BAKING, 1.0)\n",
|
||||
" decorator1 = Resource('decorator1', 1).add_skill(DECORATING, 1.0)\n",
|
||||
" waiting_space = Resource('waiting_space', 4).add_skill(PROOFING, 1.0)\n",
|
||||
" oven = Resource('oven', 4).add_skill(COOKING, 1.0)\n",
|
||||
" display_space = Resource('display_space', 12).add_skill(DISPLAY, 1.0)\n",
|
||||
" baker1 = Resource(\"baker1\", 1).add_skill(BAKING, 1.0)\n",
|
||||
" baker2 = Resource(\"baker2\", 1).add_skill(BAKING, 1.0)\n",
|
||||
" decorator1 = Resource(\"decorator1\", 1).add_skill(DECORATING, 1.0)\n",
|
||||
" waiting_space = Resource(\"waiting_space\", 4).add_skill(PROOFING, 1.0)\n",
|
||||
" oven = Resource(\"oven\", 4).add_skill(COOKING, 1.0)\n",
|
||||
" display_space = Resource(\"display_space\", 12).add_skill(DISPLAY, 1.0)\n",
|
||||
" resources = [baker1, baker2, decorator1, waiting_space, oven, display_space]\n",
|
||||
"\n",
|
||||
" # Orders\n",
|
||||
" croissant_7am = Order('croissant_7am', CROISSANT, 7 * 60, 3)\n",
|
||||
" croissant_8am = Order('croissant_8am', CROISSANT, 8 * 60, 3)\n",
|
||||
" croissant_9am = Order('croissant_9am', CROISSANT, 9 * 60, 2)\n",
|
||||
" croissant_10am = Order('croissant_10am', CROISSANT, 10 * 60, 1)\n",
|
||||
" croissant_11am = Order('croissant_11am', CROISSANT, 11 * 60, 1)\n",
|
||||
" brioche_10am = Order('brioche_10am', BRIOCHE, 10 * 60, 8)\n",
|
||||
" brioche_12pm = Order('brioche_12pm', BRIOCHE, 12 * 60, 8)\n",
|
||||
" apple_pie_1pm = Order('apple_pie_1pm', APPLE_PIE, 13 * 60, 10)\n",
|
||||
" chocolate_4pm = Order('chocolate_4pm', CHOCOLATE_CAKE, 16 * 60, 10)\n",
|
||||
" croissant_7am = Order(\"croissant_7am\", CROISSANT, 7 * 60, 3)\n",
|
||||
" croissant_8am = Order(\"croissant_8am\", CROISSANT, 8 * 60, 3)\n",
|
||||
" croissant_9am = Order(\"croissant_9am\", CROISSANT, 9 * 60, 2)\n",
|
||||
" croissant_10am = Order(\"croissant_10am\", CROISSANT, 10 * 60, 1)\n",
|
||||
" croissant_11am = Order(\"croissant_11am\", CROISSANT, 11 * 60, 1)\n",
|
||||
" brioche_10am = Order(\"brioche_10am\", BRIOCHE, 10 * 60, 8)\n",
|
||||
" brioche_12pm = Order(\"brioche_12pm\", BRIOCHE, 12 * 60, 8)\n",
|
||||
" apple_pie_1pm = Order(\"apple_pie_1pm\", APPLE_PIE, 13 * 60, 10)\n",
|
||||
" chocolate_4pm = Order(\"chocolate_4pm\", CHOCOLATE_CAKE, 16 * 60, 10)\n",
|
||||
" orders = [\n",
|
||||
" croissant_7am, croissant_8am, croissant_9am, croissant_10am,\n",
|
||||
" croissant_11am, brioche_10am, brioche_12pm, apple_pie_1pm, chocolate_4pm\n",
|
||||
" croissant_7am,\n",
|
||||
" croissant_8am,\n",
|
||||
" croissant_9am,\n",
|
||||
" croissant_10am,\n",
|
||||
" croissant_11am,\n",
|
||||
" brioche_10am,\n",
|
||||
" brioche_12pm,\n",
|
||||
" apple_pie_1pm,\n",
|
||||
" chocolate_4pm,\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" return recipes, resources, orders\n",
|
||||
@@ -276,50 +287,48 @@
|
||||
" tardiness_vars = []\n",
|
||||
" for order in orders:\n",
|
||||
" for batch in range(order.quantity):\n",
|
||||
" order_id = f'{order.unique_id}_{batch}'\n",
|
||||
" order_id = f\"{order.unique_id}_{batch}\"\n",
|
||||
" sorted_orders.append(order_id)\n",
|
||||
" previous_end = None\n",
|
||||
" due_date = order.due_date\n",
|
||||
" recipe = recipe_by_name[order.recipe_name]\n",
|
||||
" for task in recipe.tasks:\n",
|
||||
" skill_name = task.name\n",
|
||||
" suffix = f'_{order.unique_id}_batch{batch}_{skill_name}'\n",
|
||||
" suffix = f\"_{order.unique_id}_batch{batch}_{skill_name}\"\n",
|
||||
"\n",
|
||||
" start = None\n",
|
||||
" if previous_end is None:\n",
|
||||
" start = model.NewIntVar(start_work, horizon,\n",
|
||||
" f'start{suffix}')\n",
|
||||
" start = model.NewIntVar(start_work, horizon, f\"start{suffix}\")\n",
|
||||
" orders_sequence_of_events[order_id].append(\n",
|
||||
" (start, f'start{suffix}'))\n",
|
||||
" (start, f\"start{suffix}\")\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" start = previous_end\n",
|
||||
"\n",
|
||||
" size = model.NewIntVar(task.min_duration, task.max_duration,\n",
|
||||
" f'size{suffix}')\n",
|
||||
" size = model.NewIntVar(\n",
|
||||
" task.min_duration, task.max_duration, f\"size{suffix}\"\n",
|
||||
" )\n",
|
||||
" end = None\n",
|
||||
" if task == recipe.tasks[-1]:\n",
|
||||
" # The order must end after the due_date. Ideally, exactly at the\n",
|
||||
" # due_date.\n",
|
||||
" tardiness = model.NewIntVar(0, horizon - due_date,\n",
|
||||
" f'end{suffix}')\n",
|
||||
" tardiness = model.NewIntVar(0, horizon - due_date, f\"end{suffix}\")\n",
|
||||
" end = tardiness + due_date\n",
|
||||
"\n",
|
||||
" # Store the end_var for the objective.\n",
|
||||
" tardiness_vars.append(tardiness)\n",
|
||||
" else:\n",
|
||||
" end = model.NewIntVar(start_work, horizon, f'end{suffix}')\n",
|
||||
" orders_sequence_of_events[order_id].append(\n",
|
||||
" (end, f'end{suffix}'))\n",
|
||||
" end = model.NewIntVar(start_work, horizon, f\"end{suffix}\")\n",
|
||||
" orders_sequence_of_events[order_id].append((end, f\"end{suffix}\"))\n",
|
||||
" previous_end = end\n",
|
||||
"\n",
|
||||
" # Per resource copy.\n",
|
||||
" presence_literals = []\n",
|
||||
" for resource in resource_list_by_skill_name[skill_name]:\n",
|
||||
" presence = model.NewBoolVar(\n",
|
||||
" f'presence{suffix}_{resource.name}')\n",
|
||||
" presence = model.NewBoolVar(f\"presence{suffix}_{resource.name}\")\n",
|
||||
" copy = model.NewOptionalIntervalVar(\n",
|
||||
" start, size, end, presence,\n",
|
||||
" f'interval{suffix}_{resource.name}')\n",
|
||||
" start, size, end, presence, f\"interval{suffix}_{resource.name}\"\n",
|
||||
" )\n",
|
||||
" interval_list_by_resource_name[resource.name].append(copy)\n",
|
||||
" presence_literals.append(presence)\n",
|
||||
"\n",
|
||||
@@ -332,8 +341,7 @@
|
||||
" if resource.capacity == 1:\n",
|
||||
" model.AddNoOverlap(intervals)\n",
|
||||
" else:\n",
|
||||
" model.AddCumulative(intervals, [1] * len(intervals),\n",
|
||||
" resource.capacity)\n",
|
||||
" model.AddCumulative(intervals, [1] * len(intervals), resource.capacity)\n",
|
||||
"\n",
|
||||
" # The objective is to minimize the sum of the tardiness values of each jobs.\n",
|
||||
" # The tardiness is difference between the end time of an order and its\n",
|
||||
@@ -349,15 +357,15 @@
|
||||
"\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" for order_id in sorted_orders:\n",
|
||||
" print(f'{order_id}:')\n",
|
||||
" print(f\"{order_id}:\")\n",
|
||||
" for time_expr, event_id in orders_sequence_of_events[order_id]:\n",
|
||||
" time = solver.Value(time_expr)\n",
|
||||
" print(f' {event_id} at {time // 60}:{time % 60:02}')\n",
|
||||
" print(f\" {event_id} at {time // 60}:{time % 60:02}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
"\n",
|
||||
" recipes, resources, orders = set_up_data()\n",
|
||||
" solve_with_cp_sat(recipes, resources, orders)\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"CP/SAT model for the N-queens problem.\n"
|
||||
]
|
||||
},
|
||||
@@ -86,7 +87,7 @@
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"_SIZE = flags.DEFINE_integer('size', 8, 'Number of queens.')\n",
|
||||
"_SIZE = flags.DEFINE_integer(\"size\", 8, \"Number of queens.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class NQueenSolutionPrinter(cp_model.CpSolverSolutionCallback):\n",
|
||||
@@ -103,8 +104,10 @@
|
||||
"\n",
|
||||
" def on_solution_callback(self):\n",
|
||||
" current_time = time.time()\n",
|
||||
" print('Solution %i, time = %f s' %\n",
|
||||
" (self.__solution_count, current_time - self.__start_time))\n",
|
||||
" print(\n",
|
||||
" \"Solution %i, time = %f s\"\n",
|
||||
" % (self.__solution_count, current_time - self.__start_time)\n",
|
||||
" )\n",
|
||||
" self.__solution_count += 1\n",
|
||||
"\n",
|
||||
" all_queens = range(len(self.__queens))\n",
|
||||
@@ -112,9 +115,9 @@
|
||||
" for j in all_queens:\n",
|
||||
" if self.Value(self.__queens[j]) == i:\n",
|
||||
" # There is a queen in column j, row i.\n",
|
||||
" print('Q', end=' ')\n",
|
||||
" print(\"Q\", end=\" \")\n",
|
||||
" else:\n",
|
||||
" print('_', end=' ')\n",
|
||||
" print(\"_\", end=\" \")\n",
|
||||
" print()\n",
|
||||
" print()\n",
|
||||
"\n",
|
||||
@@ -127,9 +130,7 @@
|
||||
"\n",
|
||||
" ### Creates the variables.\n",
|
||||
" # The array index is the column, and the value is the row.\n",
|
||||
" queens = [\n",
|
||||
" model.NewIntVar(0, board_size - 1, 'x%i' % i) for i in range(board_size)\n",
|
||||
" ]\n",
|
||||
" queens = [model.NewIntVar(0, board_size - 1, \"x%i\" % i) for i in range(board_size)]\n",
|
||||
"\n",
|
||||
" ### Creates the constraints.\n",
|
||||
"\n",
|
||||
@@ -141,8 +142,8 @@
|
||||
" diag1 = []\n",
|
||||
" diag2 = []\n",
|
||||
" for i in range(board_size):\n",
|
||||
" q1 = model.NewIntVar(0, 2 * board_size, 'diag1_%i' % i)\n",
|
||||
" q2 = model.NewIntVar(-board_size, board_size, 'diag2_%i' % i)\n",
|
||||
" q1 = model.NewIntVar(0, 2 * board_size, \"diag1_%i\" % i)\n",
|
||||
" q2 = model.NewIntVar(-board_size, board_size, \"diag2_%i\" % i)\n",
|
||||
" diag1.append(q1)\n",
|
||||
" diag2.append(q2)\n",
|
||||
" model.Add(q1 == queens[i] + i)\n",
|
||||
@@ -159,11 +160,11 @@
|
||||
" solver.Solve(model, solution_printer)\n",
|
||||
"\n",
|
||||
" print()\n",
|
||||
" print('Statistics')\n",
|
||||
" print(' - conflicts : %i' % solver.NumConflicts())\n",
|
||||
" print(' - branches : %i' % solver.NumBranches())\n",
|
||||
" print(' - wall time : %f s' % solver.WallTime())\n",
|
||||
" print(' - solutions found : %i' % solution_printer.SolutionCount())\n",
|
||||
" print(\"Statistics\")\n",
|
||||
" print(\" - conflicts : %i\" % solver.NumConflicts())\n",
|
||||
" print(\" - branches : %i\" % solver.NumBranches())\n",
|
||||
" print(\" - wall time : %f s\" % solver.WallTime())\n",
|
||||
" print(\" - solutions found : %i\" % solution_printer.SolutionCount())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Simple prize collecting TSP problem with a max distance.\n"
|
||||
]
|
||||
},
|
||||
@@ -87,6 +88,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"DISTANCE_MATRIX = [\n",
|
||||
" # fmt:off\n",
|
||||
" [0, 10938, 4542, 2835, 29441, 2171, 1611, 9208, 9528, 11111, 16120, 22606, 22127, 20627, 21246, 23387, 16697, 33609, 26184, 24772, 22644, 20655, 30492, 23296, 32979, 18141, 19248, 17129, 17192, 15645, 12658, 11210, 12094, 13175, 18162, 4968, 12308, 10084, 13026, 15056],\n",
|
||||
" [10938, 0, 6422, 9742, 18988, 12974, 11216, 19715, 19004, 18271, 25070, 31971, 31632, 30571, 31578, 33841, 27315, 43964, 36944, 35689, 33569, 31481, 41360, 33760, 43631, 28730, 29976, 27803, 28076, 26408, 23504, 22025, 22000, 13197, 14936, 15146, 23246, 20956, 23963, 25994],\n",
|
||||
" [4542, 6422, 0, 3644, 25173, 6552, 5092, 13584, 13372, 13766, 19805, 26537, 26117, 24804, 25590, 27784, 21148, 37981, 30693, 29315, 27148, 25071, 34943, 27472, 37281, 22389, 23592, 21433, 21655, 20011, 17087, 15612, 15872, 11653, 15666, 8842, 16843, 14618, 17563, 19589],\n",
|
||||
@@ -127,7 +129,8 @@
|
||||
" [10084, 20956, 14618, 12135, 38935, 8306, 9793, 2615, 5850, 10467, 9918, 14568, 13907, 11803, 11750, 13657, 6901, 23862, 16125, 14748, 12981, 11624, 21033, 15358, 24144, 10304, 10742, 9094, 8042, 7408, 4580, 4072, 8446, 20543, 26181, 7668, 2747, 0, 3330, 5313],\n",
|
||||
" [13026, 23963, 17563, 14771, 42160, 11069, 12925, 5730, 8778, 13375, 11235, 14366, 13621, 11188, 10424, 11907, 5609, 21861, 13624, 11781, 9718, 8304, 17737, 12200, 20816, 7330, 7532, 6117, 4735, 4488, 2599, 3355, 7773, 22186, 27895, 9742, 726, 3330, 0, 2042],\n",
|
||||
" [15056, 25994, 19589, 16743, 44198, 13078, 14967, 7552, 10422, 14935, 11891, 14002, 13225, 10671, 9475, 10633, 5084, 20315, 11866, 9802, 7682, 6471, 15720, 10674, 18908, 6204, 6000, 5066, 3039, 3721, 3496, 4772, 8614, 23805, 29519, 11614, 2749, 5313, 2042, 0],\n",
|
||||
"] # yapf: disable\n",
|
||||
" # fmt:on\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"MAX_DISTANCE = 80_000\n",
|
||||
"\n",
|
||||
@@ -139,22 +142,22 @@
|
||||
"def print_solution(solver, visited_nodes, used_arcs, num_nodes):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" # Display dropped nodes.\n",
|
||||
" dropped_nodes = 'Dropped nodes:'\n",
|
||||
" dropped_nodes = \"Dropped nodes:\"\n",
|
||||
" for i in range(num_nodes):\n",
|
||||
" if i == 0:\n",
|
||||
" continue\n",
|
||||
" if not solver.BooleanValue(visited_nodes[i]):\n",
|
||||
" dropped_nodes += f' {i}({VISIT_VALUES[i]})'\n",
|
||||
" dropped_nodes += f\" {i}({VISIT_VALUES[i]})\"\n",
|
||||
" print(dropped_nodes)\n",
|
||||
" # Display routes\n",
|
||||
" current_node = 0\n",
|
||||
" plan_output = 'Route for vehicle 0:\\n'\n",
|
||||
" plan_output = \"Route for vehicle 0:\\n\"\n",
|
||||
" route_distance = 0\n",
|
||||
" value_collected = 0\n",
|
||||
" route_is_finished = False\n",
|
||||
" while not route_is_finished:\n",
|
||||
" value_collected += VISIT_VALUES[current_node]\n",
|
||||
" plan_output += f' {current_node} ->'\n",
|
||||
" plan_output += f\" {current_node} ->\"\n",
|
||||
" # find next node\n",
|
||||
" for node in range(num_nodes):\n",
|
||||
" if node == current_node:\n",
|
||||
@@ -165,9 +168,9 @@
|
||||
" if current_node == 0:\n",
|
||||
" route_is_finished = True\n",
|
||||
" break\n",
|
||||
" plan_output += f' {current_node}\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" plan_output += f'Value collected: {value_collected}/{sum(VISIT_VALUES)}\\n'\n",
|
||||
" plan_output += f\" {current_node}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" plan_output += f\"Value collected: {value_collected}/{sum(VISIT_VALUES)}\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -175,7 +178,7 @@
|
||||
" \"\"\"Entry point of the program.\"\"\"\n",
|
||||
" num_nodes = len(DISTANCE_MATRIX)\n",
|
||||
" all_nodes = range(num_nodes)\n",
|
||||
" print(f'Num nodes = {num_nodes}')\n",
|
||||
" print(f\"Num nodes = {num_nodes}\")\n",
|
||||
"\n",
|
||||
" # Model.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
@@ -188,8 +191,8 @@
|
||||
" # Create the circuit constraint.\n",
|
||||
" arcs = []\n",
|
||||
" for i in all_nodes:\n",
|
||||
" is_visited = model.NewBoolVar(f'{i} is visited')\n",
|
||||
" arcs.append([i, i, is_visited.Not()])\n",
|
||||
" is_visited = model.NewBoolVar(f\"{i} is visited\")\n",
|
||||
" arcs.append((i, i, is_visited.Not()))\n",
|
||||
"\n",
|
||||
" obj_vars.append(is_visited)\n",
|
||||
" obj_coeffs.append(VISIT_VALUES[i])\n",
|
||||
@@ -199,8 +202,8 @@
|
||||
" if i == j:\n",
|
||||
" used_arcs[i, j] = is_visited.Not()\n",
|
||||
" continue\n",
|
||||
" arc_is_used = model.NewBoolVar(f'{j} follows {i}')\n",
|
||||
" arcs.append([i, j, arc_is_used])\n",
|
||||
" arc_is_used = model.NewBoolVar(f\"{j} follows {i}\")\n",
|
||||
" arcs.append((i, j, arc_is_used))\n",
|
||||
"\n",
|
||||
" obj_vars.append(arc_is_used)\n",
|
||||
" obj_coeffs.append(-DISTANCE_MATRIX[i][j])\n",
|
||||
@@ -213,13 +216,16 @@
|
||||
"\n",
|
||||
" # limit the route distance\n",
|
||||
" model.Add(\n",
|
||||
" sum(used_arcs[i, j] * DISTANCE_MATRIX[i][j]\n",
|
||||
" sum(\n",
|
||||
" used_arcs[i, j] * DISTANCE_MATRIX[i][j]\n",
|
||||
" for i in all_nodes\n",
|
||||
" for j in all_nodes) <= MAX_DISTANCE)\n",
|
||||
" for j in all_nodes\n",
|
||||
" )\n",
|
||||
" <= MAX_DISTANCE\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Maximize visited node values minus the travelled distance.\n",
|
||||
" model.Maximize(\n",
|
||||
" sum(obj_vars[i] * obj_coeffs[i] for i in range(len(obj_vars))))\n",
|
||||
" model.Maximize(sum(obj_vars[i] * obj_coeffs[i] for i in range(len(obj_vars))))\n",
|
||||
"\n",
|
||||
" # Solve and print out the solution.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
@@ -235,7 +241,7 @@
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" prize_collecting_tsp()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Simple prize collecting VRP problem with a max distance.\n"
|
||||
]
|
||||
},
|
||||
@@ -87,6 +88,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"DISTANCE_MATRIX = [\n",
|
||||
" # fmt:off\n",
|
||||
" [0, 10938, 4542, 2835, 29441, 2171, 1611, 9208, 9528, 11111, 16120, 22606, 22127, 20627, 21246, 23387, 16697, 33609, 26184, 24772, 22644, 20655, 30492, 23296, 32979, 18141, 19248, 17129, 17192, 15645, 12658, 11210, 12094, 13175, 18162, 4968, 12308, 10084, 13026, 15056],\n",
|
||||
" [10938, 0, 6422, 9742, 18988, 12974, 11216, 19715, 19004, 18271, 25070, 31971, 31632, 30571, 31578, 33841, 27315, 43964, 36944, 35689, 33569, 31481, 41360, 33760, 43631, 28730, 29976, 27803, 28076, 26408, 23504, 22025, 22000, 13197, 14936, 15146, 23246, 20956, 23963, 25994],\n",
|
||||
" [4542, 6422, 0, 3644, 25173, 6552, 5092, 13584, 13372, 13766, 19805, 26537, 26117, 24804, 25590, 27784, 21148, 37981, 30693, 29315, 27148, 25071, 34943, 27472, 37281, 22389, 23592, 21433, 21655, 20011, 17087, 15612, 15872, 11653, 15666, 8842, 16843, 14618, 17563, 19589],\n",
|
||||
@@ -127,7 +129,8 @@
|
||||
" [10084, 20956, 14618, 12135, 38935, 8306, 9793, 2615, 5850, 10467, 9918, 14568, 13907, 11803, 11750, 13657, 6901, 23862, 16125, 14748, 12981, 11624, 21033, 15358, 24144, 10304, 10742, 9094, 8042, 7408, 4580, 4072, 8446, 20543, 26181, 7668, 2747, 0, 3330, 5313],\n",
|
||||
" [13026, 23963, 17563, 14771, 42160, 11069, 12925, 5730, 8778, 13375, 11235, 14366, 13621, 11188, 10424, 11907, 5609, 21861, 13624, 11781, 9718, 8304, 17737, 12200, 20816, 7330, 7532, 6117, 4735, 4488, 2599, 3355, 7773, 22186, 27895, 9742, 726, 3330, 0, 2042],\n",
|
||||
" [15056, 25994, 19589, 16743, 44198, 13078, 14967, 7552, 10422, 14935, 11891, 14002, 13225, 10671, 9475, 10633, 5084, 20315, 11866, 9802, 7682, 6471, 15720, 10674, 18908, 6204, 6000, 5066, 3039, 3721, 3496, 4772, 8614, 23805, 29519, 11614, 2749, 5313, 2042, 0],\n",
|
||||
"] # yapf: disable\n",
|
||||
" # fmt:on\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"MAX_DISTANCE = 80_000\n",
|
||||
"\n",
|
||||
@@ -139,29 +142,28 @@
|
||||
"def print_solution(solver, visited_nodes, used_arcs, num_nodes, num_vehicles):\n",
|
||||
" \"\"\"Prints solution on console.\"\"\"\n",
|
||||
" # Display dropped nodes.\n",
|
||||
" dropped_nodes = 'Dropped nodes:'\n",
|
||||
" dropped_nodes = \"Dropped nodes:\"\n",
|
||||
" for node in range(num_nodes):\n",
|
||||
" if node == 0:\n",
|
||||
" continue\n",
|
||||
" is_visited = sum([\n",
|
||||
" solver.BooleanValue(visited_nodes[v][node])\n",
|
||||
" for v in range(num_vehicles)\n",
|
||||
" ])\n",
|
||||
" is_visited = sum(\n",
|
||||
" [solver.BooleanValue(visited_nodes[v][node]) for v in range(num_vehicles)]\n",
|
||||
" )\n",
|
||||
" if not is_visited:\n",
|
||||
" dropped_nodes += f' {node}({VISIT_VALUES[node]})'\n",
|
||||
" dropped_nodes += f\" {node}({VISIT_VALUES[node]})\"\n",
|
||||
" print(dropped_nodes)\n",
|
||||
" # Display routes\n",
|
||||
" total_distance = 0\n",
|
||||
" total_value_collected = 0\n",
|
||||
" for v in range(num_vehicles):\n",
|
||||
" current_node = 0\n",
|
||||
" plan_output = f'Route for vehicle {v}:\\n'\n",
|
||||
" plan_output = f\"Route for vehicle {v}:\\n\"\n",
|
||||
" route_distance = 0\n",
|
||||
" value_collected = 0\n",
|
||||
" route_is_finished = False\n",
|
||||
" while not route_is_finished:\n",
|
||||
" value_collected += VISIT_VALUES[current_node]\n",
|
||||
" plan_output += f' {current_node} ->'\n",
|
||||
" plan_output += f\" {current_node} ->\"\n",
|
||||
" # find next node\n",
|
||||
" for node in range(num_nodes):\n",
|
||||
" if node == current_node:\n",
|
||||
@@ -172,21 +174,21 @@
|
||||
" if current_node == 0:\n",
|
||||
" route_is_finished = True\n",
|
||||
" break\n",
|
||||
" plan_output += f' {current_node}\\n'\n",
|
||||
" plan_output += f'Distance of the route: {route_distance}m\\n'\n",
|
||||
" plan_output += f'Value collected: {value_collected}\\n'\n",
|
||||
" plan_output += f\" {current_node}\\n\"\n",
|
||||
" plan_output += f\"Distance of the route: {route_distance}m\\n\"\n",
|
||||
" plan_output += f\"Value collected: {value_collected}\\n\"\n",
|
||||
" print(plan_output)\n",
|
||||
" total_distance += route_distance\n",
|
||||
" total_value_collected += value_collected\n",
|
||||
" print(f'Total Distance: {total_distance}m')\n",
|
||||
" print(f'Total Value collected: {total_value_collected}/{sum(VISIT_VALUES)}')\n",
|
||||
" print(f\"Total Distance: {total_distance}m\")\n",
|
||||
" print(f\"Total Value collected: {total_value_collected}/{sum(VISIT_VALUES)}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def prize_collecting_vrp():\n",
|
||||
" \"\"\"Entry point of the program.\"\"\"\n",
|
||||
" num_nodes = len(DISTANCE_MATRIX)\n",
|
||||
" num_vehicles = 4\n",
|
||||
" print(f'Num nodes = {num_nodes}')\n",
|
||||
" print(f\"Num nodes = {num_nodes}\")\n",
|
||||
"\n",
|
||||
" # Model.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
@@ -203,8 +205,8 @@
|
||||
" used_arcs[v] = {}\n",
|
||||
" arcs = []\n",
|
||||
" for i in all_nodes:\n",
|
||||
" is_visited = model.NewBoolVar(f'{i} is visited')\n",
|
||||
" arcs.append([i, i, is_visited.Not()])\n",
|
||||
" is_visited = model.NewBoolVar(f\"{i} is visited\")\n",
|
||||
" arcs.append((i, i, is_visited.Not()))\n",
|
||||
"\n",
|
||||
" obj_vars.append(is_visited)\n",
|
||||
" obj_coeffs.append(VISIT_VALUES[i])\n",
|
||||
@@ -214,8 +216,8 @@
|
||||
" if i == j:\n",
|
||||
" used_arcs[v][i, j] = is_visited.Not()\n",
|
||||
" continue\n",
|
||||
" arc_is_used = model.NewBoolVar(f'{j} follows {i}')\n",
|
||||
" arcs.append([i, j, arc_is_used])\n",
|
||||
" arc_is_used = model.NewBoolVar(f\"{j} follows {i}\")\n",
|
||||
" arcs.append((i, j, arc_is_used))\n",
|
||||
"\n",
|
||||
" obj_vars.append(arc_is_used)\n",
|
||||
" obj_coeffs.append(-DISTANCE_MATRIX[i][j])\n",
|
||||
@@ -228,18 +230,20 @@
|
||||
"\n",
|
||||
" # limit the route distance\n",
|
||||
" model.Add(\n",
|
||||
" sum(used_arcs[v][i, j] * DISTANCE_MATRIX[i][j]\n",
|
||||
" sum(\n",
|
||||
" used_arcs[v][i, j] * DISTANCE_MATRIX[i][j]\n",
|
||||
" for i in all_nodes\n",
|
||||
" for j in all_nodes) <= MAX_DISTANCE)\n",
|
||||
" for j in all_nodes\n",
|
||||
" )\n",
|
||||
" <= MAX_DISTANCE\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Each node is visited at most once\n",
|
||||
" for node in range(1, num_nodes):\n",
|
||||
" model.AddAtMostOne(\n",
|
||||
" [visited_nodes[v][node] for v in range(num_vehicles)])\n",
|
||||
" model.AddAtMostOne([visited_nodes[v][node] for v in range(num_vehicles)])\n",
|
||||
"\n",
|
||||
" # Maximize visited node values minus the travelled distance.\n",
|
||||
" model.Maximize(\n",
|
||||
" sum(obj_vars[i] * obj_coeffs[i] for i in range(len(obj_vars))))\n",
|
||||
" model.Maximize(sum(obj_vars[i] * obj_coeffs[i] for i in range(len(obj_vars))))\n",
|
||||
"\n",
|
||||
" # Solve and print out the solution.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
@@ -249,13 +253,12 @@
|
||||
"\n",
|
||||
" status = solver.Solve(model)\n",
|
||||
" if status == cp_model.FEASIBLE or status == cp_model.OPTIMAL:\n",
|
||||
" print_solution(solver, visited_nodes, used_arcs, num_nodes,\n",
|
||||
" num_vehicles)\n",
|
||||
" print_solution(solver, visited_nodes, used_arcs, num_nodes, num_vehicles)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" prize_collecting_vrp()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"MaxFlow and MinCostFlow examples.\n"
|
||||
]
|
||||
},
|
||||
@@ -89,7 +90,7 @@
|
||||
"\n",
|
||||
"def max_flow_api():\n",
|
||||
" \"\"\"MaxFlow simple interface example.\"\"\"\n",
|
||||
" print('MaxFlow on a simple network.')\n",
|
||||
" print(\"MaxFlow on a simple network.\")\n",
|
||||
" tails = [0, 0, 0, 0, 1, 2, 3, 3, 4]\n",
|
||||
" heads = [1, 2, 3, 4, 3, 4, 4, 5, 5]\n",
|
||||
" capacities = [5, 8, 5, 3, 4, 5, 6, 6, 4]\n",
|
||||
@@ -98,52 +99,54 @@
|
||||
" for i in range(0, len(tails)):\n",
|
||||
" smf.add_arc_with_capacity(tails[i], heads[i], capacities[i])\n",
|
||||
" if smf.solve(0, 5) == smf.OPTIMAL:\n",
|
||||
" print('Total flow', smf.optimal_flow(), '/', expected_total_flow)\n",
|
||||
" print(\"Total flow\", smf.optimal_flow(), \"/\", expected_total_flow)\n",
|
||||
" for i in range(smf.num_arcs()):\n",
|
||||
" print('From source %d to target %d: %d / %d' %\n",
|
||||
" (smf.tail(i), smf.head(i), smf.flow(i), smf.capacity(i)))\n",
|
||||
" print('Source side min-cut:', smf.get_source_side_min_cut())\n",
|
||||
" print('Sink side min-cut:', smf.get_sink_side_min_cut())\n",
|
||||
" print(\n",
|
||||
" \"From source %d to target %d: %d / %d\"\n",
|
||||
" % (smf.tail(i), smf.head(i), smf.flow(i), smf.capacity(i))\n",
|
||||
" )\n",
|
||||
" print(\"Source side min-cut:\", smf.get_source_side_min_cut())\n",
|
||||
" print(\"Sink side min-cut:\", smf.get_sink_side_min_cut())\n",
|
||||
" else:\n",
|
||||
" print('There was an issue with the max flow input.')\n",
|
||||
" print(\"There was an issue with the max flow input.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def min_cost_flow_api():\n",
|
||||
" \"\"\"MinCostFlow simple interface example.\n",
|
||||
"\n",
|
||||
" Note that this example is actually a linear sum assignment example and will\n",
|
||||
" be more efficiently solved with the pywrapgraph.LinearSumAssignment class.\n",
|
||||
" \"\"\"\n",
|
||||
" print('MinCostFlow on 4x4 matrix.')\n",
|
||||
" Note that this example is actually a linear sum assignment example and will\n",
|
||||
" be more efficiently solved with the pywrapgraph.LinearSumAssignment class.\n",
|
||||
" \"\"\"\n",
|
||||
" print(\"MinCostFlow on 4x4 matrix.\")\n",
|
||||
" num_sources = 4\n",
|
||||
" num_targets = 4\n",
|
||||
" costs = [[90, 75, 75, 80], [35, 85, 55, 65], [125, 95, 90, 105],\n",
|
||||
" [45, 110, 95, 115]]\n",
|
||||
" costs = [[90, 75, 75, 80], [35, 85, 55, 65], [125, 95, 90, 105], [45, 110, 95, 115]]\n",
|
||||
" expected_cost = 275\n",
|
||||
" smcf = min_cost_flow.SimpleMinCostFlow()\n",
|
||||
" for source in range(0, num_sources):\n",
|
||||
" for target in range(0, num_targets):\n",
|
||||
" smcf.add_arc_with_capacity_and_unit_cost(source,\n",
|
||||
" num_sources + target, 1,\n",
|
||||
" costs[source][target])\n",
|
||||
" smcf.add_arc_with_capacity_and_unit_cost(\n",
|
||||
" source, num_sources + target, 1, costs[source][target]\n",
|
||||
" )\n",
|
||||
" for node in range(0, num_sources):\n",
|
||||
" smcf.set_node_supply(node, 1)\n",
|
||||
" smcf.set_node_supply(num_sources + node, -1)\n",
|
||||
" status = smcf.solve()\n",
|
||||
" if status == smcf.OPTIMAL:\n",
|
||||
" print('Total flow', smcf.optimal_cost(), '/', expected_cost)\n",
|
||||
" print(\"Total flow\", smcf.optimal_cost(), \"/\", expected_cost)\n",
|
||||
" for i in range(0, smcf.num_arcs()):\n",
|
||||
" if smcf.flow(i) > 0:\n",
|
||||
" print('From source %d to target %d: cost %d' %\n",
|
||||
" (smcf.tail(i), smcf.head(i) - num_sources,\n",
|
||||
" smcf.unit_cost(i)))\n",
|
||||
" print(\n",
|
||||
" \"From source %d to target %d: cost %d\"\n",
|
||||
" % (smcf.tail(i), smcf.head(i) - num_sources, smcf.unit_cost(i))\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('There was an issue with the min cost flow input.')\n",
|
||||
" print(\"There was an issue with the min cost flow input.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" max_flow_api()\n",
|
||||
" min_cost_flow_api()\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solves a Qubo program using the CP-SAT solver.\n"
|
||||
]
|
||||
},
|
||||
@@ -86,6 +87,7 @@
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"RAW_DATA = [\n",
|
||||
" # fmt:off\n",
|
||||
" [\n",
|
||||
" 0, 0, 49.774821, -59.5968886, -46.0773896, 0, -65.166109, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 47.0957778, 15.259961, -98.7983264, 0, 0, 0, -20.7757184,\n",
|
||||
@@ -714,6 +716,7 @@
|
||||
" 0, 84.8495464, 0, 0, 0, 0, 0, 0, 33.0825986, 46.995148, 0, 0, 0,\n",
|
||||
" -4.243203, 0, 0, -24.0124188\n",
|
||||
" ]\n",
|
||||
" # fmt:on\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -725,7 +728,7 @@
|
||||
"\n",
|
||||
" num_vars = len(RAW_DATA)\n",
|
||||
" all_vars = range(num_vars)\n",
|
||||
" variables = [model.NewBoolVar('x_%i' % i) for i in all_vars]\n",
|
||||
" variables = [model.NewBoolVar(\"x_%i\" % i) for i in all_vars]\n",
|
||||
"\n",
|
||||
" obj_vars = []\n",
|
||||
" obj_coeffs = []\n",
|
||||
@@ -737,7 +740,7 @@
|
||||
" if coeff == 0.0:\n",
|
||||
" continue\n",
|
||||
" x_j = variables[j]\n",
|
||||
" var = model.NewBoolVar('')\n",
|
||||
" var = model.NewBoolVar(\"\")\n",
|
||||
" model.AddBoolOr([x_i.Not(), x_j.Not(), var])\n",
|
||||
" model.AddImplication(var, x_i)\n",
|
||||
" model.AddImplication(var, x_j)\n",
|
||||
@@ -750,8 +753,7 @@
|
||||
" obj_vars.append(variables[i])\n",
|
||||
" obj_coeffs.append(self_coeff)\n",
|
||||
"\n",
|
||||
" model.Minimize(\n",
|
||||
" sum(obj_vars[i] * obj_coeffs[i] for i in range(len(obj_vars))))\n",
|
||||
" model.Minimize(sum(obj_vars[i] * obj_coeffs[i] for i in range(len(obj_vars))))\n",
|
||||
"\n",
|
||||
" ### Solve model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
@@ -763,7 +765,7 @@
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" solve_qubo()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Sat based solver for the RCPSP problems (see rcpsp.proto).\n",
|
||||
"\n",
|
||||
"Introduction to the problem:\n",
|
||||
@@ -94,39 +95,47 @@
|
||||
"\n",
|
||||
"from google.protobuf import text_format\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"from ortools.scheduling import pywraprcpsp\n",
|
||||
"from ortools.scheduling import rcpsp_pb2\n",
|
||||
"from ortools.scheduling.python import rcpsp\n",
|
||||
"\n",
|
||||
"class FLAGS: pass\n",
|
||||
"\n",
|
||||
"_INPUT = flags.DEFINE_string('input', '', 'Input file to parse and solve.')\n",
|
||||
"_INPUT = flags.DEFINE_string(\"input\", \"\", \"Input file to parse and solve.\")\n",
|
||||
"_OUTPUT_PROTO = flags.DEFINE_string(\n",
|
||||
" 'output_proto', '', 'Output file to write the cp_model proto to.')\n",
|
||||
"_PARAMS = flags.DEFINE_string('params', '', 'Sat solver parameters.')\n",
|
||||
" \"output_proto\", \"\", \"Output file to write the cp_model proto to.\"\n",
|
||||
")\n",
|
||||
"_PARAMS = flags.DEFINE_string(\"params\", \"\", \"Sat solver parameters.\")\n",
|
||||
"_USE_INTERVAL_MAKESPAN = flags.DEFINE_bool(\n",
|
||||
" 'use_interval_makespan', True,\n",
|
||||
" 'Whether we encode the makespan using an interval or not.')\n",
|
||||
"_HORIZON = flags.DEFINE_integer('horizon', -1, 'Force horizon.')\n",
|
||||
" \"use_interval_makespan\",\n",
|
||||
" False,\n",
|
||||
" \"Whether we encode the makespan using an interval or not.\",\n",
|
||||
")\n",
|
||||
"_HORIZON = flags.DEFINE_integer(\"horizon\", -1, \"Force horizon.\")\n",
|
||||
"_ADD_REDUNDANT_ENERGETIC_CONSTRAINTS = flags.DEFINE_bool(\n",
|
||||
" 'add_redundant_energetic_constraints', False,\n",
|
||||
" 'Add redundant energetic constraints on the pairs of tasks extracted from' +\n",
|
||||
" ' precedence graph.')\n",
|
||||
" \"add_redundant_energetic_constraints\",\n",
|
||||
" False,\n",
|
||||
" \"Add redundant energetic constraints on the pairs of tasks extracted from\"\n",
|
||||
" + \" precedence graph.\",\n",
|
||||
")\n",
|
||||
"_DELAY_TIME_LIMIT = flags.DEFINE_float(\n",
|
||||
" 'delay_time_limit', 0.0,\n",
|
||||
" 'Time limit when computing min delay between tasks.' +\n",
|
||||
" ' A non-positive time limit disable min delays computation.')\n",
|
||||
" \"delay_time_limit\",\n",
|
||||
" 0.0,\n",
|
||||
" \"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",
|
||||
" 'preemptive_lb_time_limit', 0.0,\n",
|
||||
" 'Time limit when computing a preemptive schedule lower bound.' +\n",
|
||||
" ' A non-positive time limit disable this computation.')\n",
|
||||
" \"preemptive_lb_time_limit\",\n",
|
||||
" 0.0,\n",
|
||||
" \"Time limit when computing a preemptive schedule lower bound.\"\n",
|
||||
" + \" A non-positive time limit disable this computation.\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def PrintProblemStatistics(problem):\n",
|
||||
" \"\"\"Display various statistics on the problem.\"\"\"\n",
|
||||
"\n",
|
||||
" # Determine problem type.\n",
|
||||
" problem_type = ('Resource Investment Problem'\n",
|
||||
" if problem.is_resource_investment else 'RCPSP')\n",
|
||||
" problem_type = (\n",
|
||||
" \"Resource Investment Problem\" if problem.is_resource_investment else \"RCPSP\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" num_resources = len(problem.resources)\n",
|
||||
" num_tasks = len(problem.tasks) - 2 # 2 sentinels.\n",
|
||||
@@ -146,43 +155,39 @@
|
||||
" tasks_with_delay += 1\n",
|
||||
"\n",
|
||||
" if problem.is_rcpsp_max:\n",
|
||||
" problem_type += '/Max delay'\n",
|
||||
" problem_type += \"/Max delay\"\n",
|
||||
" # We print 2 less tasks as these are sentinel tasks that are not counted in\n",
|
||||
" # the description of the rcpsp models.\n",
|
||||
" if problem.is_consumer_producer:\n",
|
||||
" print(f'Solving {problem_type} with:')\n",
|
||||
" print(f' - {num_resources} reservoir resources')\n",
|
||||
" print(f' - {num_tasks} tasks')\n",
|
||||
" print(f\"Solving {problem_type} with:\")\n",
|
||||
" print(f\" - {num_resources} reservoir resources\")\n",
|
||||
" print(f\" - {num_tasks} tasks\")\n",
|
||||
" else:\n",
|
||||
" print(f'Solving {problem_type} with:')\n",
|
||||
" print(f' - {num_resources} renewable resources')\n",
|
||||
" print(f' - {num_tasks} tasks')\n",
|
||||
" print(f\"Solving {problem_type} with:\")\n",
|
||||
" print(f\" - {num_resources} renewable resources\")\n",
|
||||
" print(f\" - {num_tasks} tasks\")\n",
|
||||
" if tasks_with_alternatives:\n",
|
||||
" print(\n",
|
||||
" f' - {tasks_with_alternatives} tasks with alternative resources'\n",
|
||||
" )\n",
|
||||
" print(f\" - {tasks_with_alternatives} tasks with alternative resources\")\n",
|
||||
" if variable_duration_tasks:\n",
|
||||
" print(\n",
|
||||
" f' - {variable_duration_tasks} tasks with variable durations'\n",
|
||||
" )\n",
|
||||
" print(f\" - {variable_duration_tasks} tasks with variable durations\")\n",
|
||||
" if tasks_with_delay:\n",
|
||||
" print(f' - {tasks_with_delay} tasks with successor delays')\n",
|
||||
" print(f\" - {tasks_with_delay} tasks with successor delays\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def AnalyseDependencyGraph(problem):\n",
|
||||
" \"\"\"Analyses the dependency graph to improve the model.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" problem: the protobuf of the problem to solve.\n",
|
||||
" Args:\n",
|
||||
" problem: the protobuf of the problem to solve.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" a list of (task1, task2, in_between_tasks) with task2 and indirect successor\n",
|
||||
" of task1, and in_between_tasks being the list of all tasks after task1 and\n",
|
||||
" before task2.\n",
|
||||
" \"\"\"\n",
|
||||
" Returns:\n",
|
||||
" a list of (task1, task2, in_between_tasks) with task2 and indirect successor\n",
|
||||
" of task1, and in_between_tasks being the list of all tasks after task1 and\n",
|
||||
" before task2.\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" num_nodes = len(problem.tasks)\n",
|
||||
" print(f'Analysing the dependency graph over {num_nodes} nodes')\n",
|
||||
" print(f\"Analysing the dependency graph over {num_nodes} nodes\")\n",
|
||||
"\n",
|
||||
" ins = collections.defaultdict(list)\n",
|
||||
" outs = collections.defaultdict(list)\n",
|
||||
@@ -239,49 +244,50 @@
|
||||
"\n",
|
||||
" # Sort entries lexicographically by (len(common), source, sink)\n",
|
||||
" def Price(entry):\n",
|
||||
" return (num_nodes * num_nodes * len(entry[2]) + num_nodes * entry[0] +\n",
|
||||
" entry[1])\n",
|
||||
" return num_nodes * num_nodes * len(entry[2]) + num_nodes * entry[0] + entry[1]\n",
|
||||
"\n",
|
||||
" result.sort(key=Price)\n",
|
||||
" print(f' - created {len(result)} pairs of nodes to examine', flush=True)\n",
|
||||
" print(f\" - created {len(result)} pairs of nodes to examine\", flush=True)\n",
|
||||
" return result, after\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def SolveRcpsp(problem,\n",
|
||||
" proto_file,\n",
|
||||
" params,\n",
|
||||
" active_tasks,\n",
|
||||
" source,\n",
|
||||
" sink,\n",
|
||||
" intervals_of_tasks,\n",
|
||||
" delays,\n",
|
||||
" in_main_solve=False,\n",
|
||||
" initial_solution=None,\n",
|
||||
" lower_bound=0):\n",
|
||||
"def SolveRcpsp(\n",
|
||||
" problem,\n",
|
||||
" proto_file,\n",
|
||||
" params,\n",
|
||||
" active_tasks,\n",
|
||||
" source,\n",
|
||||
" sink,\n",
|
||||
" intervals_of_tasks,\n",
|
||||
" delays,\n",
|
||||
" in_main_solve=False,\n",
|
||||
" initial_solution=None,\n",
|
||||
" lower_bound=0,\n",
|
||||
"):\n",
|
||||
" \"\"\"Parse and solve a given RCPSP problem in proto format.\n",
|
||||
"\n",
|
||||
" The model will only look at the tasks {source} + {sink} + active_tasks, and\n",
|
||||
" ignore all others.\n",
|
||||
" The model will only look at the tasks {source} + {sink} + active_tasks, and\n",
|
||||
" ignore all others.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" problem: the description of the model to solve in protobuf format\n",
|
||||
" proto_file: the name of the file to export the CpModel proto to.\n",
|
||||
" params: the string representation of the parameters to pass to the sat\n",
|
||||
" solver.\n",
|
||||
" active_tasks: the set of active tasks to consider.\n",
|
||||
" source: the source task in the graph. Its end will be forced to 0.\n",
|
||||
" sink: the sink task of the graph. Its start is the makespan of the problem.\n",
|
||||
" intervals_of_tasks: a heuristic lists of (task1, task2, tasks) used to add\n",
|
||||
" redundant energetic equations to the model.\n",
|
||||
" delays: a list of (task1, task2, min_delays) used to add extended precedence\n",
|
||||
" constraints (start(task2) >= end(task1) + min_delay).\n",
|
||||
" in_main_solve: indicates if this is the main solve procedure.\n",
|
||||
" initial_solution: A valid assignment used to hint the search.\n",
|
||||
" lower_bound: A valid lower bound of the makespan objective.\n",
|
||||
" Args:\n",
|
||||
" problem: the description of the model to solve in protobuf format\n",
|
||||
" proto_file: the name of the file to export the CpModel proto to.\n",
|
||||
" params: the string representation of the parameters to pass to the sat\n",
|
||||
" solver.\n",
|
||||
" active_tasks: the set of active tasks to consider.\n",
|
||||
" source: the source task in the graph. Its end will be forced to 0.\n",
|
||||
" sink: the sink task of the graph. Its start is the makespan of the problem.\n",
|
||||
" intervals_of_tasks: a heuristic lists of (task1, task2, tasks) used to add\n",
|
||||
" redundant energetic equations to the model.\n",
|
||||
" delays: a list of (task1, task2, min_delays) used to add extended precedence\n",
|
||||
" constraints (start(task2) >= end(task1) + min_delay).\n",
|
||||
" in_main_solve: indicates if this is the main solve procedure.\n",
|
||||
" initial_solution: A valid assignment used to hint the search.\n",
|
||||
" lower_bound: A valid lower bound of the makespan objective.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" (lower_bound of the objective, best solution found, asssignment)\n",
|
||||
" \"\"\"\n",
|
||||
" Returns:\n",
|
||||
" (lower_bound of the objective, best solution found, assignment)\n",
|
||||
" \"\"\"\n",
|
||||
" # Create the model.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
" model.SetName(problem.name)\n",
|
||||
@@ -306,7 +312,7 @@
|
||||
" for d in rd.min_delays:\n",
|
||||
" horizon += abs(d)\n",
|
||||
" if in_main_solve:\n",
|
||||
" print(f'Horizon = {horizon}', flush=True)\n",
|
||||
" print(f\"Horizon = {horizon}\", flush=True)\n",
|
||||
"\n",
|
||||
" # Containers.\n",
|
||||
" task_starts = {}\n",
|
||||
@@ -329,15 +335,13 @@
|
||||
" num_recipes = len(task.recipes)\n",
|
||||
" all_recipes = range(num_recipes)\n",
|
||||
"\n",
|
||||
" start_var = model.NewIntVar(0, horizon, f'start_of_task_{t}')\n",
|
||||
" end_var = model.NewIntVar(0, horizon, f'end_of_task_{t}')\n",
|
||||
" start_var = model.NewIntVar(0, horizon, f\"start_of_task_{t}\")\n",
|
||||
" end_var = model.NewIntVar(0, horizon, f\"end_of_task_{t}\")\n",
|
||||
"\n",
|
||||
" literals = []\n",
|
||||
" if num_recipes > 1:\n",
|
||||
" # Create one literal per recipe.\n",
|
||||
" literals = [\n",
|
||||
" model.NewBoolVar(f'is_present_{t}_{r}') for r in all_recipes\n",
|
||||
" ]\n",
|
||||
" literals = [model.NewBoolVar(f\"is_present_{t}_{r}\") for r in all_recipes]\n",
|
||||
"\n",
|
||||
" # Exactly one recipe must be performed.\n",
|
||||
" model.AddExactlyOne(literals)\n",
|
||||
@@ -357,17 +361,19 @@
|
||||
" # Create the duration variable from the accumulated durations.\n",
|
||||
" duration_var = model.NewIntVarFromDomain(\n",
|
||||
" cp_model.Domain.FromValues(task_to_recipe_durations[t]),\n",
|
||||
" f'duration_of_task_{t}')\n",
|
||||
" f\"duration_of_task_{t}\",\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Link the recipe literals and the duration_var.\n",
|
||||
" for r in range(num_recipes):\n",
|
||||
" model.Add(\n",
|
||||
" duration_var == task_to_recipe_durations[t][r]).OnlyEnforceIf(\n",
|
||||
" literals[r])\n",
|
||||
" model.Add(duration_var == task_to_recipe_durations[t][r]).OnlyEnforceIf(\n",
|
||||
" literals[r]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create the interval of the task.\n",
|
||||
" task_interval = model.NewIntervalVar(start_var, duration_var, end_var,\n",
|
||||
" f'task_interval_{t}')\n",
|
||||
" task_interval = model.NewIntervalVar(\n",
|
||||
" start_var, duration_var, end_var, f\"task_interval_{t}\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Store task variables.\n",
|
||||
" task_starts[t] = start_var\n",
|
||||
@@ -381,33 +387,38 @@
|
||||
" demands = [demand_matrix[(res, recipe)] for recipe in all_recipes]\n",
|
||||
" task_resource_to_fixed_demands[(t, res)] = demands\n",
|
||||
" demand_var = model.NewIntVarFromDomain(\n",
|
||||
" cp_model.Domain.FromValues(demands), f'demand_{t}_{res}')\n",
|
||||
" cp_model.Domain.FromValues(demands), f\"demand_{t}_{res}\"\n",
|
||||
" )\n",
|
||||
" task_to_resource_demands[t].append(demand_var)\n",
|
||||
"\n",
|
||||
" # Link the recipe literals and the demand_var.\n",
|
||||
" for r in all_recipes:\n",
|
||||
" model.Add(demand_var == demand_matrix[(res, r)]).OnlyEnforceIf(\n",
|
||||
" literals[r])\n",
|
||||
" literals[r]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" resource_to_sum_of_demand_max[res] += max(demands)\n",
|
||||
"\n",
|
||||
" # Create the energy expression for (task, resource):\n",
|
||||
" for res in all_resources:\n",
|
||||
" task_resource_to_energy[(t, res)] = sum(\n",
|
||||
" literals[r] * task_to_recipe_durations[t][r] *\n",
|
||||
" task_resource_to_fixed_demands[(t, res)][r]\n",
|
||||
" for r in all_recipes)\n",
|
||||
" literals[r]\n",
|
||||
" * task_to_recipe_durations[t][r]\n",
|
||||
" * task_resource_to_fixed_demands[(t, res)][r]\n",
|
||||
" for r in all_recipes\n",
|
||||
" )\n",
|
||||
" task_resource_to_max_energy[(t, res)] = max(\n",
|
||||
" task_to_recipe_durations[t][r] *\n",
|
||||
" task_resource_to_fixed_demands[(t, res)][r]\n",
|
||||
" for r in all_recipes)\n",
|
||||
" task_to_recipe_durations[t][r]\n",
|
||||
" * task_resource_to_fixed_demands[(t, res)][r]\n",
|
||||
" for r in all_recipes\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Create makespan variable\n",
|
||||
" makespan = model.NewIntVar(lower_bound, horizon, 'makespan')\n",
|
||||
" makespan_size = model.NewIntVar(1, horizon, 'interval_makespan_size')\n",
|
||||
" interval_makespan = model.NewIntervalVar(makespan, makespan_size,\n",
|
||||
" model.NewConstant(horizon + 1),\n",
|
||||
" 'interval_makespan')\n",
|
||||
" makespan = model.NewIntVar(lower_bound, horizon, \"makespan\")\n",
|
||||
" makespan_size = model.NewIntVar(1, horizon, \"interval_makespan_size\")\n",
|
||||
" interval_makespan = model.NewIntervalVar(\n",
|
||||
" makespan, makespan_size, model.NewConstant(horizon + 1), \"interval_makespan\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Add precedences.\n",
|
||||
" if problem.is_rcpsp_max:\n",
|
||||
@@ -417,8 +428,7 @@
|
||||
" task = problem.tasks[task_id]\n",
|
||||
" num_modes = len(task.recipes)\n",
|
||||
"\n",
|
||||
" for successor_index in range(len(task.successors)):\n",
|
||||
" next_id = task.successors[successor_index]\n",
|
||||
" for successor_index, next_id in enumerate(task.successors):\n",
|
||||
" delay_matrix = task.successor_delays[successor_index]\n",
|
||||
" num_next_modes = len(problem.tasks[next_id].recipes)\n",
|
||||
" for m1 in range(num_modes):\n",
|
||||
@@ -429,8 +439,7 @@
|
||||
" model.Add(s1 + delay <= makespan).OnlyEnforceIf(p1)\n",
|
||||
" else:\n",
|
||||
" for m2 in range(num_next_modes):\n",
|
||||
" delay = delay_matrix.recipe_delays[m1].min_delays[\n",
|
||||
" m2]\n",
|
||||
" delay = delay_matrix.recipe_delays[m1].min_delays[m2]\n",
|
||||
" s2 = task_starts[next_id]\n",
|
||||
" p2 = task_to_presence_literals[next_id][m2]\n",
|
||||
" model.Add(s1 + delay <= s2).OnlyEnforceIf([p1, p2])\n",
|
||||
@@ -452,18 +461,16 @@
|
||||
" resource = problem.resources[res]\n",
|
||||
" c = resource.max_capacity\n",
|
||||
" if c == -1:\n",
|
||||
" print(f'No capacity: {resource}')\n",
|
||||
" print(f\"No capacity: {resource}\")\n",
|
||||
" c = resource_to_sum_of_demand_max[res]\n",
|
||||
"\n",
|
||||
" # RIP problems have only renewable resources, and no makespan.\n",
|
||||
" if problem.is_resource_investment or resource.renewable:\n",
|
||||
" intervals = [task_intervals[t] for t in all_active_tasks]\n",
|
||||
" demands = [\n",
|
||||
" task_to_resource_demands[t][res] for t in all_active_tasks\n",
|
||||
" ]\n",
|
||||
" demands = [task_to_resource_demands[t][res] for t in all_active_tasks]\n",
|
||||
"\n",
|
||||
" if problem.is_resource_investment:\n",
|
||||
" capacity = model.NewIntVar(0, c, f'capacity_of_{res}')\n",
|
||||
" capacity = model.NewIntVar(0, c, f\"capacity_of_{res}\")\n",
|
||||
" model.AddCumulative(intervals, demands, capacity)\n",
|
||||
" capacities.append(capacity)\n",
|
||||
" max_cost += c * resource.unit_cost\n",
|
||||
@@ -481,24 +488,32 @@
|
||||
" if task_resource_to_fixed_demands[(t, res)][0]:\n",
|
||||
" reservoir_starts.append(task_starts[t])\n",
|
||||
" reservoir_demands.append(\n",
|
||||
" task_resource_to_fixed_demands[(t, res)][0])\n",
|
||||
" model.AddReservoirConstraint(reservoir_starts,\n",
|
||||
" reservoir_demands,\n",
|
||||
" resource.min_capacity,\n",
|
||||
" resource.max_capacity)\n",
|
||||
" task_resource_to_fixed_demands[(t, res)][0]\n",
|
||||
" )\n",
|
||||
" model.AddReservoirConstraint(\n",
|
||||
" reservoir_starts,\n",
|
||||
" reservoir_demands,\n",
|
||||
" resource.min_capacity,\n",
|
||||
" resource.max_capacity,\n",
|
||||
" )\n",
|
||||
" else: # No producer-consumer. We just sum the demands.\n",
|
||||
" model.Add(\n",
|
||||
" cp_model.LinearExpr.Sum([\n",
|
||||
" task_to_resource_demands[t][res]\n",
|
||||
" for t in all_active_tasks\n",
|
||||
" ]) <= c)\n",
|
||||
" cp_model.LinearExpr.Sum(\n",
|
||||
" [task_to_resource_demands[t][res] for t in all_active_tasks]\n",
|
||||
" )\n",
|
||||
" <= c\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Objective.\n",
|
||||
" if problem.is_resource_investment:\n",
|
||||
" objective = model.NewIntVar(0, max_cost, 'capacity_costs')\n",
|
||||
" model.Add(objective == sum(problem.resources[i].unit_cost *\n",
|
||||
" capacities[i]\n",
|
||||
" for i in range(len(capacities))))\n",
|
||||
" objective = model.NewIntVar(0, max_cost, \"capacity_costs\")\n",
|
||||
" model.Add(\n",
|
||||
" objective\n",
|
||||
" == sum(\n",
|
||||
" problem.resources[i].unit_cost * capacities[i]\n",
|
||||
" for i in range(len(capacities))\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" objective = makespan\n",
|
||||
"\n",
|
||||
@@ -512,8 +527,7 @@
|
||||
" elif local_start in active_tasks and local_end == sink:\n",
|
||||
" model.Add(makespan >= task_ends[local_start] + min_delay)\n",
|
||||
" elif local_start in active_tasks and local_end in active_tasks:\n",
|
||||
" model.Add(task_starts[local_end] >= task_ends[local_start] +\n",
|
||||
" min_delay)\n",
|
||||
" model.Add(task_starts[local_end] >= task_ends[local_start] + min_delay)\n",
|
||||
"\n",
|
||||
" problem_is_single_mode = True\n",
|
||||
" for t in all_active_tasks:\n",
|
||||
@@ -533,10 +547,13 @@
|
||||
" # graph, it add the energetic relaxation:\n",
|
||||
" # (start_var('end') - end_var('start')) * capacity_max >=\n",
|
||||
" # sum of linearized energies of all tasks from 'in_between_tasks'\n",
|
||||
" if (not problem.is_resource_investment and\n",
|
||||
" not problem.is_consumer_producer and\n",
|
||||
" _ADD_REDUNDANT_ENERGETIC_CONSTRAINTS.value and in_main_solve and\n",
|
||||
" not problem_is_single_mode):\n",
|
||||
" if (\n",
|
||||
" not problem.is_resource_investment\n",
|
||||
" and not problem.is_consumer_producer\n",
|
||||
" and _ADD_REDUNDANT_ENERGETIC_CONSTRAINTS.value\n",
|
||||
" and in_main_solve\n",
|
||||
" and not problem_is_single_mode\n",
|
||||
" ):\n",
|
||||
" added_constraints = 0\n",
|
||||
" ignored_constraits = 0\n",
|
||||
" for local_start, local_end, common in intervals_of_tasks:\n",
|
||||
@@ -548,19 +565,21 @@
|
||||
" if delays and (local_start, local_end) in delays:\n",
|
||||
" min_delay, _ = delays[local_start, local_end]\n",
|
||||
" sum_of_max_energies = sum(\n",
|
||||
" task_resource_to_max_energy[(t, res)] for t in common)\n",
|
||||
" task_resource_to_max_energy[(t, res)] for t in common\n",
|
||||
" )\n",
|
||||
" if sum_of_max_energies <= c * min_delay:\n",
|
||||
" ignored_constraits += 1\n",
|
||||
" continue\n",
|
||||
" model.Add(\n",
|
||||
" c *\n",
|
||||
" (task_starts[local_end] - task_ends[local_start]) >= sum(\n",
|
||||
" task_resource_to_energy[(t, res)] for t in common))\n",
|
||||
" c * (task_starts[local_end] - task_ends[local_start])\n",
|
||||
" >= sum(task_resource_to_energy[(t, res)] for t in common)\n",
|
||||
" )\n",
|
||||
" added_constraints += 1\n",
|
||||
" print(\n",
|
||||
" f'Added {added_constraints} redundant energetic constraints, and ' +\n",
|
||||
" f'ignored {ignored_constraits} constraints.',\n",
|
||||
" flush=True)\n",
|
||||
" f\"Added {added_constraints} redundant energetic constraints, and \"\n",
|
||||
" + f\"ignored {ignored_constraits} constraints.\",\n",
|
||||
" flush=True,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Add solution hint.\n",
|
||||
" if initial_solution:\n",
|
||||
@@ -572,19 +591,25 @@
|
||||
"\n",
|
||||
" # Write model to file.\n",
|
||||
" if proto_file:\n",
|
||||
" print(f'Writing proto to{proto_file}')\n",
|
||||
" print(f\"Writing proto to{proto_file}\")\n",
|
||||
" model.ExportToFile(proto_file)\n",
|
||||
"\n",
|
||||
" # Solve model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" if not _USE_INTERVAL_MAKESPAN.value:\n",
|
||||
" solver.parameters.exploit_all_precedences = True\n",
|
||||
" solver.parameters.use_hard_precedences_in_cumulative = True\n",
|
||||
" if params:\n",
|
||||
" text_format.Parse(params, solver.parameters)\n",
|
||||
" if solver.parameters.num_workers >= 16 and solver.parameters.num_workers < 24:\n",
|
||||
" solver.parameters.ignore_subsolvers.append(\"objective_lb_search\")\n",
|
||||
" solver.parameters.extra_subsolvers.append(\"objective_shaving_search\")\n",
|
||||
" if in_main_solve:\n",
|
||||
" solver.parameters.log_search_progress = True\n",
|
||||
" status = solver.Solve(model)\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" assignment = rcpsp_pb2.RcpspAssignment()\n",
|
||||
" for t in range(len(problem.tasks)):\n",
|
||||
" for t, _ in enumerate(problem.tasks):\n",
|
||||
" if t in task_starts:\n",
|
||||
" assignment.start_of_task.append(solver.Value(task_starts[t]))\n",
|
||||
" for r in range(len(task_to_presence_literals[t])):\n",
|
||||
@@ -594,25 +619,32 @@
|
||||
" else: # t is not an active task.\n",
|
||||
" assignment.start_of_task.append(0)\n",
|
||||
" assignment.selected_recipe_of_task.append(0)\n",
|
||||
" return (int(solver.BestObjectiveBound()), int(solver.ObjectiveValue()),\n",
|
||||
" assignment)\n",
|
||||
" return (\n",
|
||||
" int(solver.BestObjectiveBound()),\n",
|
||||
" int(solver.ObjectiveValue()),\n",
|
||||
" assignment,\n",
|
||||
" )\n",
|
||||
" return -1, -1, None\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def ComputeDelaysBetweenNodes(problem, task_intervals):\n",
|
||||
" \"\"\"Computes the min delays between all pairs of tasks in 'task_intervals'.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" problem: The protobuf of the model.\n",
|
||||
" task_intervals: The output of the AnalysePrecedenceGraph().\n",
|
||||
" Args:\n",
|
||||
" problem: The protobuf of the model.\n",
|
||||
" task_intervals: The output of the AnalysePrecedenceGraph().\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" a list of (task1, task2, min_delay_between_task1_and_task2)\n",
|
||||
" \"\"\"\n",
|
||||
" print('Computing the minimum delay between pairs of intervals')\n",
|
||||
" Returns:\n",
|
||||
" a list of (task1, task2, min_delay_between_task1_and_task2)\n",
|
||||
" \"\"\"\n",
|
||||
" print(\"Computing the minimum delay between pairs of intervals\")\n",
|
||||
" delays = {}\n",
|
||||
" if (problem.is_resource_investment or problem.is_consumer_producer or\n",
|
||||
" problem.is_rcpsp_max or _DELAY_TIME_LIMIT.value <= 0.0):\n",
|
||||
" if (\n",
|
||||
" problem.is_resource_investment\n",
|
||||
" or problem.is_consumer_producer\n",
|
||||
" or problem.is_rcpsp_max\n",
|
||||
" or _DELAY_TIME_LIMIT.value <= 0.0\n",
|
||||
" ):\n",
|
||||
" return delays, None, False\n",
|
||||
"\n",
|
||||
" complete_problem_assignment = None\n",
|
||||
@@ -621,9 +653,15 @@
|
||||
" optimal_found = True\n",
|
||||
" for start_task, end_task, active_tasks in task_intervals:\n",
|
||||
" min_delay, feasible_delay, assignment = SolveRcpsp(\n",
|
||||
" problem, '',\n",
|
||||
" f'num_search_workers:16,max_time_in_seconds:{_DELAY_TIME_LIMIT.value}',\n",
|
||||
" active_tasks, start_task, end_task, [], delays)\n",
|
||||
" problem,\n",
|
||||
" \"\",\n",
|
||||
" f\"num_search_workers:16,max_time_in_seconds:{_DELAY_TIME_LIMIT.value}\",\n",
|
||||
" active_tasks,\n",
|
||||
" start_task,\n",
|
||||
" end_task,\n",
|
||||
" [],\n",
|
||||
" delays,\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",
|
||||
@@ -636,9 +674,9 @@
|
||||
" num_delays_not_found += 1\n",
|
||||
" optimal_found = False\n",
|
||||
"\n",
|
||||
" print(f' - #optimal delays = {num_optimal_delays}', flush=True)\n",
|
||||
" print(f\" - #optimal delays = {num_optimal_delays}\", flush=True)\n",
|
||||
" if num_delays_not_found:\n",
|
||||
" print(f' - #not computed delays = {num_delays_not_found}', flush=True)\n",
|
||||
" print(f\" - #not computed delays = {num_delays_not_found}\", flush=True)\n",
|
||||
"\n",
|
||||
" return delays, complete_problem_assignment, optimal_found\n",
|
||||
"\n",
|
||||
@@ -654,8 +692,10 @@
|
||||
" resource = problem.resources[res]\n",
|
||||
" if not resource.renewable:\n",
|
||||
" continue\n",
|
||||
" if (sum(demand_map[(t, res)] for t in current) +\n",
|
||||
" demand_map[(candidate, res)] > resource.max_capacity):\n",
|
||||
" if (\n",
|
||||
" sum(demand_map[(t, res)] for t in current) + demand_map[(candidate, res)]\n",
|
||||
" > resource.max_capacity\n",
|
||||
" ):\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" return True\n",
|
||||
@@ -664,23 +704,26 @@
|
||||
"def ComputePreemptiveLowerBound(problem, after, lower_bound):\n",
|
||||
" \"\"\"Computes a preemtive lower bound for the makespan statically.\n",
|
||||
"\n",
|
||||
" For this, it breaks all intervals into a set of intervals of size one.\n",
|
||||
" Then it will try to assign all of them in a minimum number of configurations.\n",
|
||||
" This is a standard complete set covering using column generation approach\n",
|
||||
" where each column is a possible combination of itervals of size one.\n",
|
||||
" For this, it breaks all intervals into a set of intervals of size one.\n",
|
||||
" Then it will try to assign all of them in a minimum number of configurations.\n",
|
||||
" This is a standard complete set covering using column generation approach\n",
|
||||
" where each column is a possible combination of itervals of size one.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" problem: The probuf of the model.\n",
|
||||
" after: a task to list of task dict that contains all tasks after a given\n",
|
||||
" task.\n",
|
||||
" lower_bound: A valid lower bound of the problem. It can be 0.\n",
|
||||
" Args:\n",
|
||||
" problem: The probuf of the model.\n",
|
||||
" after: a task to list of task dict that contains all tasks after a given\n",
|
||||
" task.\n",
|
||||
" lower_bound: A valid lower bound of the problem. It can be 0.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" a valid lower bound of the problem.\n",
|
||||
" \"\"\"\n",
|
||||
" Returns:\n",
|
||||
" a valid lower bound of the problem.\n",
|
||||
" \"\"\"\n",
|
||||
" # Check this is a single mode problem.\n",
|
||||
" if (problem.is_rcpsp_max or problem.is_resource_investment or\n",
|
||||
" problem.is_consumer_producer):\n",
|
||||
" if (\n",
|
||||
" problem.is_rcpsp_max\n",
|
||||
" or problem.is_resource_investment\n",
|
||||
" or problem.is_consumer_producer\n",
|
||||
" ):\n",
|
||||
" return lower_bound\n",
|
||||
"\n",
|
||||
" demand_map = collections.defaultdict(int)\n",
|
||||
@@ -700,9 +743,11 @@
|
||||
" max_duration = max(max_duration, recipe.duration)\n",
|
||||
" sum_of_demands += demand\n",
|
||||
"\n",
|
||||
" print(f'Compute a bin-packing lower bound with {len(all_active_tasks)}' +\n",
|
||||
" ' active tasks',\n",
|
||||
" flush=True)\n",
|
||||
" print(\n",
|
||||
" f\"Compute a bin-packing lower bound with {len(all_active_tasks)}\"\n",
|
||||
" + \" active tasks\",\n",
|
||||
" flush=True,\n",
|
||||
" )\n",
|
||||
" all_combinations = []\n",
|
||||
"\n",
|
||||
" for t in all_active_tasks:\n",
|
||||
@@ -714,7 +759,7 @@
|
||||
"\n",
|
||||
" all_combinations.extend(new_combinations)\n",
|
||||
"\n",
|
||||
" print(f' - created {len(all_combinations)} combinations')\n",
|
||||
" print(f\" - created {len(all_combinations)} combinations\")\n",
|
||||
" if len(all_combinations) > 5000000:\n",
|
||||
" return lower_bound # Abort if too large.\n",
|
||||
"\n",
|
||||
@@ -725,7 +770,7 @@
|
||||
" # do not use that column.\n",
|
||||
" # 2/ Merge all task with exactly same demands into one.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
" model.SetName(f'lower_bound_{problem.name}')\n",
|
||||
" model.SetName(f\"lower_bound_{problem.name}\")\n",
|
||||
"\n",
|
||||
" vars_per_task = collections.defaultdict(list)\n",
|
||||
" all_vars = []\n",
|
||||
@@ -733,7 +778,7 @@
|
||||
" min_duration = max_duration\n",
|
||||
" for t in c:\n",
|
||||
" min_duration = min(min_duration, duration_map[t])\n",
|
||||
" count = model.NewIntVar(0, min_duration, f'count_{t}')\n",
|
||||
" count = model.NewIntVar(0, min_duration, f\"count_{c}\")\n",
|
||||
" all_vars.append(count)\n",
|
||||
" for t in c:\n",
|
||||
" vars_per_task[t].append(count)\n",
|
||||
@@ -743,8 +788,7 @@
|
||||
" model.Add(sum(vars_per_task[t]) >= duration_map[t])\n",
|
||||
"\n",
|
||||
" # Objective\n",
|
||||
" objective_var = model.NewIntVar(lower_bound, sum_of_demands,\n",
|
||||
" 'objective_var')\n",
|
||||
" objective_var = model.NewIntVar(lower_bound, sum_of_demands, \"objective_var\")\n",
|
||||
" model.Add(objective_var == sum(all_vars))\n",
|
||||
"\n",
|
||||
" model.Minimize(objective_var)\n",
|
||||
@@ -755,24 +799,24 @@
|
||||
" solver.parameters.max_time_in_seconds = _PREEMPTIVE_LB_TIME_LIMIT.value\n",
|
||||
" status = solver.Solve(model)\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" status_str = 'optimal' if status == cp_model.OPTIMAL else ''\n",
|
||||
" status_str = \"optimal\" if status == cp_model.OPTIMAL else \"\"\n",
|
||||
" lower_bound = max(lower_bound, int(solver.BestObjectiveBound()))\n",
|
||||
" print(f' - {status_str} static lower bound = {lower_bound}',\n",
|
||||
" flush=True)\n",
|
||||
" print(f\" - {status_str} static lower bound = {lower_bound}\", flush=True)\n",
|
||||
"\n",
|
||||
" return lower_bound\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(_):\n",
|
||||
" rcpsp_parser = pywraprcpsp.RcpspParser()\n",
|
||||
" rcpsp_parser.ParseFile(_INPUT.value)\n",
|
||||
" rcpsp_parser = rcpsp.RcpspParser()\n",
|
||||
" rcpsp_parser.parse_file(_INPUT.value)\n",
|
||||
"\n",
|
||||
" problem = rcpsp_parser.Problem()\n",
|
||||
" problem = rcpsp_parser.problem()\n",
|
||||
" PrintProblemStatistics(problem)\n",
|
||||
"\n",
|
||||
" intervals_of_tasks, after = AnalyseDependencyGraph(problem)\n",
|
||||
" delays, initial_solution, optimal_found = ComputeDelaysBetweenNodes(\n",
|
||||
" problem, intervals_of_tasks)\n",
|
||||
" problem, intervals_of_tasks\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" last_task = len(problem.tasks) - 1\n",
|
||||
" key = (0, last_task)\n",
|
||||
@@ -780,17 +824,19 @@
|
||||
" if not optimal_found and _PREEMPTIVE_LB_TIME_LIMIT.value > 0.0:\n",
|
||||
" lower_bound = ComputePreemptiveLowerBound(problem, after, lower_bound)\n",
|
||||
"\n",
|
||||
" SolveRcpsp(problem=problem,\n",
|
||||
" proto_file=_OUTPUT_PROTO.value,\n",
|
||||
" params=_PARAMS.value,\n",
|
||||
" active_tasks=set(range(1, last_task)),\n",
|
||||
" source=0,\n",
|
||||
" sink=last_task,\n",
|
||||
" intervals_of_tasks=intervals_of_tasks,\n",
|
||||
" delays=delays,\n",
|
||||
" in_main_solve=True,\n",
|
||||
" initial_solution=initial_solution,\n",
|
||||
" lower_bound=lower_bound)\n",
|
||||
" SolveRcpsp(\n",
|
||||
" problem=problem,\n",
|
||||
" proto_file=_OUTPUT_PROTO.value,\n",
|
||||
" params=_PARAMS.value,\n",
|
||||
" active_tasks=set(range(1, last_task)),\n",
|
||||
" source=0,\n",
|
||||
" sink=last_task,\n",
|
||||
" intervals_of_tasks=intervals_of_tasks,\n",
|
||||
" delays=delays,\n",
|
||||
" in_main_solve=True,\n",
|
||||
" initial_solution=initial_solution,\n",
|
||||
" lower_bound=lower_bound,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Creates a shift scheduling problem and solves it.\n"
|
||||
]
|
||||
},
|
||||
@@ -86,28 +87,30 @@
|
||||
"from google.protobuf import text_format\n",
|
||||
"\n",
|
||||
"_OUTPUT_PROTO = flags.DEFINE_string(\n",
|
||||
" 'output_proto', '', 'Output file to write the cp_model proto to.')\n",
|
||||
"_PARAMS = flags.DEFINE_string('params', 'max_time_in_seconds:10.0',\n",
|
||||
" 'Sat solver parameters.')\n",
|
||||
" \"output_proto\", \"\", \"Output file to write the cp_model proto to.\"\n",
|
||||
")\n",
|
||||
"_PARAMS = flags.DEFINE_string(\n",
|
||||
" \"params\", \"max_time_in_seconds:10.0\", \"Sat solver parameters.\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def negated_bounded_span(works, start, length):\n",
|
||||
" \"\"\"Filters an isolated sub-sequence of variables assined to True.\n",
|
||||
"\n",
|
||||
" Extract the span of Boolean variables [start, start + length), negate them,\n",
|
||||
" and if there is variables to the left/right of this span, surround the span by\n",
|
||||
" them in non negated form.\n",
|
||||
" Extract the span of Boolean variables [start, start + length), negate them,\n",
|
||||
" and if there is variables to the left/right of this span, surround the span by\n",
|
||||
" them in non negated form.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" works: a list of variables to extract the span from.\n",
|
||||
" start: the start to the span.\n",
|
||||
" length: the length of the span.\n",
|
||||
" Args:\n",
|
||||
" works: a list of variables to extract the span from.\n",
|
||||
" start: the start to the span.\n",
|
||||
" length: the length of the span.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" a list of variables which conjunction will be false if the sub-list is\n",
|
||||
" assigned to True, and correctly bounded by variables assigned to False,\n",
|
||||
" or by the start or end of works.\n",
|
||||
" \"\"\"\n",
|
||||
" Returns:\n",
|
||||
" a list of variables which conjunction will be false if the sub-list is\n",
|
||||
" assigned to True, and correctly bounded by variables assigned to False,\n",
|
||||
" or by the start or end of works.\n",
|
||||
" \"\"\"\n",
|
||||
" sequence = []\n",
|
||||
" # Left border (start of works, or works[start - 1])\n",
|
||||
" if start > 0:\n",
|
||||
@@ -120,35 +123,36 @@
|
||||
" return sequence\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def add_soft_sequence_constraint(model, works, hard_min, soft_min, min_cost,\n",
|
||||
" soft_max, hard_max, max_cost, prefix):\n",
|
||||
"def add_soft_sequence_constraint(\n",
|
||||
" model, works, hard_min, soft_min, min_cost, soft_max, hard_max, max_cost, prefix\n",
|
||||
"):\n",
|
||||
" \"\"\"Sequence constraint on true variables with soft and hard bounds.\n",
|
||||
"\n",
|
||||
" This constraint look at every maximal contiguous sequence of variables\n",
|
||||
" assigned to true. If forbids sequence of length < hard_min or > hard_max.\n",
|
||||
" Then it creates penalty terms if the length is < soft_min or > soft_max.\n",
|
||||
" This constraint look at every maximal contiguous sequence of variables\n",
|
||||
" assigned to true. If forbids sequence of length < hard_min or > hard_max.\n",
|
||||
" Then it creates penalty terms if the length is < soft_min or > soft_max.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" model: the sequence constraint is built on this model.\n",
|
||||
" works: a list of Boolean variables.\n",
|
||||
" hard_min: any sequence of true variables must have a length of at least\n",
|
||||
" hard_min.\n",
|
||||
" soft_min: any sequence should have a length of at least soft_min, or a\n",
|
||||
" linear penalty on the delta will be added to the objective.\n",
|
||||
" min_cost: the coefficient of the linear penalty if the length is less than\n",
|
||||
" soft_min.\n",
|
||||
" soft_max: any sequence should have a length of at most soft_max, or a linear\n",
|
||||
" penalty on the delta will be added to the objective.\n",
|
||||
" hard_max: any sequence of true variables must have a length of at most\n",
|
||||
" hard_max.\n",
|
||||
" max_cost: the coefficient of the linear penalty if the length is more than\n",
|
||||
" soft_max.\n",
|
||||
" prefix: a base name for penalty literals.\n",
|
||||
" Args:\n",
|
||||
" model: the sequence constraint is built on this model.\n",
|
||||
" works: a list of Boolean variables.\n",
|
||||
" hard_min: any sequence of true variables must have a length of at least\n",
|
||||
" hard_min.\n",
|
||||
" soft_min: any sequence should have a length of at least soft_min, or a\n",
|
||||
" linear penalty on the delta will be added to the objective.\n",
|
||||
" min_cost: the coefficient of the linear penalty if the length is less than\n",
|
||||
" soft_min.\n",
|
||||
" soft_max: any sequence should have a length of at most soft_max, or a linear\n",
|
||||
" penalty on the delta will be added to the objective.\n",
|
||||
" hard_max: any sequence of true variables must have a length of at most\n",
|
||||
" hard_max.\n",
|
||||
" max_cost: the coefficient of the linear penalty if the length is more than\n",
|
||||
" soft_max.\n",
|
||||
" prefix: a base name for penalty literals.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" a tuple (variables_list, coefficient_list) containing the different\n",
|
||||
" penalties created by the sequence constraint.\n",
|
||||
" \"\"\"\n",
|
||||
" Returns:\n",
|
||||
" a tuple (variables_list, coefficient_list) containing the different\n",
|
||||
" penalties created by the sequence constraint.\n",
|
||||
" \"\"\"\n",
|
||||
" cost_literals = []\n",
|
||||
" cost_coefficients = []\n",
|
||||
"\n",
|
||||
@@ -162,7 +166,7 @@
|
||||
" for length in range(hard_min, soft_min):\n",
|
||||
" for start in range(len(works) - length + 1):\n",
|
||||
" span = negated_bounded_span(works, start, length)\n",
|
||||
" name = ': under_span(start=%i, length=%i)' % (start, length)\n",
|
||||
" name = \": under_span(start=%i, length=%i)\" % (start, length)\n",
|
||||
" lit = model.NewBoolVar(prefix + name)\n",
|
||||
" span.append(lit)\n",
|
||||
" model.AddBoolOr(span)\n",
|
||||
@@ -176,7 +180,7 @@
|
||||
" for length in range(soft_max + 1, hard_max + 1):\n",
|
||||
" for start in range(len(works) - length + 1):\n",
|
||||
" span = negated_bounded_span(works, start, length)\n",
|
||||
" name = ': over_span(start=%i, length=%i)' % (start, length)\n",
|
||||
" name = \": over_span(start=%i, length=%i)\" % (start, length)\n",
|
||||
" lit = model.NewBoolVar(prefix + name)\n",
|
||||
" span.append(lit)\n",
|
||||
" model.AddBoolOr(span)\n",
|
||||
@@ -186,61 +190,61 @@
|
||||
"\n",
|
||||
" # Just forbid any sequence of true variables with length hard_max + 1\n",
|
||||
" for start in range(len(works) - hard_max):\n",
|
||||
" model.AddBoolOr(\n",
|
||||
" [works[i].Not() for i in range(start, start + hard_max + 1)])\n",
|
||||
" model.AddBoolOr([works[i].Not() for i in range(start, start + hard_max + 1)])\n",
|
||||
" return cost_literals, cost_coefficients\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def add_soft_sum_constraint(model, works, hard_min, soft_min, min_cost,\n",
|
||||
" soft_max, hard_max, max_cost, prefix):\n",
|
||||
"def add_soft_sum_constraint(\n",
|
||||
" model, works, hard_min, soft_min, min_cost, soft_max, hard_max, max_cost, prefix\n",
|
||||
"):\n",
|
||||
" \"\"\"Sum constraint with soft and hard bounds.\n",
|
||||
"\n",
|
||||
" This constraint counts the variables assigned to true from works.\n",
|
||||
" If forbids sum < hard_min or > hard_max.\n",
|
||||
" Then it creates penalty terms if the sum is < soft_min or > soft_max.\n",
|
||||
" This constraint counts the variables assigned to true from works.\n",
|
||||
" If forbids sum < hard_min or > hard_max.\n",
|
||||
" Then it creates penalty terms if the sum is < soft_min or > soft_max.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" model: the sequence constraint is built on this model.\n",
|
||||
" works: a list of Boolean variables.\n",
|
||||
" hard_min: any sequence of true variables must have a sum of at least\n",
|
||||
" hard_min.\n",
|
||||
" soft_min: any sequence should have a sum of at least soft_min, or a linear\n",
|
||||
" penalty on the delta will be added to the objective.\n",
|
||||
" min_cost: the coefficient of the linear penalty if the sum is less than\n",
|
||||
" soft_min.\n",
|
||||
" soft_max: any sequence should have a sum of at most soft_max, or a linear\n",
|
||||
" penalty on the delta will be added to the objective.\n",
|
||||
" hard_max: any sequence of true variables must have a sum of at most\n",
|
||||
" hard_max.\n",
|
||||
" max_cost: the coefficient of the linear penalty if the sum is more than\n",
|
||||
" soft_max.\n",
|
||||
" prefix: a base name for penalty variables.\n",
|
||||
" Args:\n",
|
||||
" model: the sequence constraint is built on this model.\n",
|
||||
" works: a list of Boolean variables.\n",
|
||||
" hard_min: any sequence of true variables must have a sum of at least\n",
|
||||
" hard_min.\n",
|
||||
" soft_min: any sequence should have a sum of at least soft_min, or a linear\n",
|
||||
" penalty on the delta will be added to the objective.\n",
|
||||
" min_cost: the coefficient of the linear penalty if the sum is less than\n",
|
||||
" soft_min.\n",
|
||||
" soft_max: any sequence should have a sum of at most soft_max, or a linear\n",
|
||||
" penalty on the delta will be added to the objective.\n",
|
||||
" hard_max: any sequence of true variables must have a sum of at most\n",
|
||||
" hard_max.\n",
|
||||
" max_cost: the coefficient of the linear penalty if the sum is more than\n",
|
||||
" soft_max.\n",
|
||||
" prefix: a base name for penalty variables.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" a tuple (variables_list, coefficient_list) containing the different\n",
|
||||
" penalties created by the sequence constraint.\n",
|
||||
" \"\"\"\n",
|
||||
" Returns:\n",
|
||||
" a tuple (variables_list, coefficient_list) containing the different\n",
|
||||
" penalties created by the sequence constraint.\n",
|
||||
" \"\"\"\n",
|
||||
" cost_variables = []\n",
|
||||
" cost_coefficients = []\n",
|
||||
" sum_var = model.NewIntVar(hard_min, hard_max, '')\n",
|
||||
" sum_var = model.NewIntVar(hard_min, hard_max, \"\")\n",
|
||||
" # This adds the hard constraints on the sum.\n",
|
||||
" model.Add(sum_var == sum(works))\n",
|
||||
"\n",
|
||||
" # Penalize sums below the soft_min target.\n",
|
||||
" if soft_min > hard_min and min_cost > 0:\n",
|
||||
" delta = model.NewIntVar(-len(works), len(works), '')\n",
|
||||
" delta = model.NewIntVar(-len(works), len(works), \"\")\n",
|
||||
" model.Add(delta == soft_min - sum_var)\n",
|
||||
" # TODO(user): Compare efficiency with only excess >= soft_min - sum_var.\n",
|
||||
" excess = model.NewIntVar(0, 7, prefix + ': under_sum')\n",
|
||||
" excess = model.NewIntVar(0, 7, prefix + \": under_sum\")\n",
|
||||
" model.AddMaxEquality(excess, [delta, 0])\n",
|
||||
" cost_variables.append(excess)\n",
|
||||
" cost_coefficients.append(min_cost)\n",
|
||||
"\n",
|
||||
" # Penalize sums above the soft_max target.\n",
|
||||
" if soft_max < hard_max and max_cost > 0:\n",
|
||||
" delta = model.NewIntVar(-7, 7, '')\n",
|
||||
" delta = model.NewIntVar(-7, 7, \"\")\n",
|
||||
" model.Add(delta == sum_var - soft_max)\n",
|
||||
" excess = model.NewIntVar(0, 7, prefix + ': over_sum')\n",
|
||||
" excess = model.NewIntVar(0, 7, prefix + \": over_sum\")\n",
|
||||
" model.AddMaxEquality(excess, [delta, 0])\n",
|
||||
" cost_variables.append(excess)\n",
|
||||
" cost_coefficients.append(max_cost)\n",
|
||||
@@ -253,7 +257,7 @@
|
||||
" # Data\n",
|
||||
" num_employees = 8\n",
|
||||
" num_weeks = 3\n",
|
||||
" shifts = ['O', 'M', 'A', 'N']\n",
|
||||
" shifts = [\"O\", \"M\", \"A\", \"N\"]\n",
|
||||
"\n",
|
||||
" # Fixed assignment: (employee, shift, day).\n",
|
||||
" # This fixes the first 2 days of the schedule.\n",
|
||||
@@ -286,7 +290,7 @@
|
||||
" (5, 3, 10, -2),\n",
|
||||
" # Employee 2 does not want a night shift on the first Friday (positive\n",
|
||||
" # weight).\n",
|
||||
" (2, 3, 4, 4)\n",
|
||||
" (2, 3, 4, 4),\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" # Shift constraints on continuous sequence :\n",
|
||||
@@ -343,7 +347,7 @@
|
||||
" for e in range(num_employees):\n",
|
||||
" for s in range(num_shifts):\n",
|
||||
" for d in range(num_days):\n",
|
||||
" work[e, s, d] = model.NewBoolVar('work%i_%i_%i' % (e, s, d))\n",
|
||||
" work[e, s, d] = model.NewBoolVar(\"work%i_%i_%i\" % (e, s, d))\n",
|
||||
"\n",
|
||||
" # Linear terms of the objective in a minimization context.\n",
|
||||
" obj_int_vars = []\n",
|
||||
@@ -371,9 +375,16 @@
|
||||
" for e in range(num_employees):\n",
|
||||
" works = [work[e, shift, d] for d in range(num_days)]\n",
|
||||
" variables, coeffs = add_soft_sequence_constraint(\n",
|
||||
" model, works, hard_min, soft_min, min_cost, soft_max, hard_max,\n",
|
||||
" model,\n",
|
||||
" works,\n",
|
||||
" hard_min,\n",
|
||||
" soft_min,\n",
|
||||
" min_cost,\n",
|
||||
" soft_max,\n",
|
||||
" hard_max,\n",
|
||||
" max_cost,\n",
|
||||
" 'shift_constraint(employee %i, shift %i)' % (e, shift))\n",
|
||||
" \"shift_constraint(employee %i, shift %i)\" % (e, shift),\n",
|
||||
" )\n",
|
||||
" obj_bool_vars.extend(variables)\n",
|
||||
" obj_bool_coeffs.extend(coeffs)\n",
|
||||
"\n",
|
||||
@@ -384,10 +395,17 @@
|
||||
" for w in range(num_weeks):\n",
|
||||
" works = [work[e, shift, d + w * 7] for d in range(7)]\n",
|
||||
" variables, coeffs = add_soft_sum_constraint(\n",
|
||||
" model, works, hard_min, soft_min, min_cost, soft_max,\n",
|
||||
" hard_max, max_cost,\n",
|
||||
" 'weekly_sum_constraint(employee %i, shift %i, week %i)' %\n",
|
||||
" (e, shift, w))\n",
|
||||
" model,\n",
|
||||
" works,\n",
|
||||
" hard_min,\n",
|
||||
" soft_min,\n",
|
||||
" min_cost,\n",
|
||||
" soft_max,\n",
|
||||
" hard_max,\n",
|
||||
" max_cost,\n",
|
||||
" \"weekly_sum_constraint(employee %i, shift %i, week %i)\"\n",
|
||||
" % (e, shift, w),\n",
|
||||
" )\n",
|
||||
" obj_int_vars.extend(variables)\n",
|
||||
" obj_int_coeffs.extend(coeffs)\n",
|
||||
"\n",
|
||||
@@ -396,14 +414,15 @@
|
||||
" for e in range(num_employees):\n",
|
||||
" for d in range(num_days - 1):\n",
|
||||
" transition = [\n",
|
||||
" work[e, previous_shift, d].Not(), work[e, next_shift,\n",
|
||||
" d + 1].Not()\n",
|
||||
" work[e, previous_shift, d].Not(),\n",
|
||||
" work[e, next_shift, d + 1].Not(),\n",
|
||||
" ]\n",
|
||||
" if cost == 0:\n",
|
||||
" model.AddBoolOr(transition)\n",
|
||||
" else:\n",
|
||||
" trans_var = model.NewBoolVar(\n",
|
||||
" 'transition (employee=%i, day=%i)' % (e, d))\n",
|
||||
" \"transition (employee=%i, day=%i)\" % (e, d)\n",
|
||||
" )\n",
|
||||
" transition.append(trans_var)\n",
|
||||
" model.AddBoolOr(transition)\n",
|
||||
" obj_bool_vars.append(trans_var)\n",
|
||||
@@ -416,28 +435,25 @@
|
||||
" works = [work[e, s, w * 7 + d] for e in range(num_employees)]\n",
|
||||
" # Ignore Off shift.\n",
|
||||
" min_demand = weekly_cover_demands[d][s - 1]\n",
|
||||
" worked = model.NewIntVar(min_demand, num_employees, '')\n",
|
||||
" worked = model.NewIntVar(min_demand, num_employees, \"\")\n",
|
||||
" model.Add(worked == sum(works))\n",
|
||||
" over_penalty = excess_cover_penalties[s - 1]\n",
|
||||
" if over_penalty > 0:\n",
|
||||
" name = 'excess_demand(shift=%i, week=%i, day=%i)' % (s, w,\n",
|
||||
" d)\n",
|
||||
" excess = model.NewIntVar(0, num_employees - min_demand,\n",
|
||||
" name)\n",
|
||||
" name = \"excess_demand(shift=%i, week=%i, day=%i)\" % (s, w, d)\n",
|
||||
" excess = model.NewIntVar(0, num_employees - min_demand, name)\n",
|
||||
" model.Add(excess == worked - min_demand)\n",
|
||||
" obj_int_vars.append(excess)\n",
|
||||
" obj_int_coeffs.append(over_penalty)\n",
|
||||
"\n",
|
||||
" # Objective\n",
|
||||
" model.Minimize(\n",
|
||||
" sum(obj_bool_vars[i] * obj_bool_coeffs[i]\n",
|
||||
" for i in range(len(obj_bool_vars))) +\n",
|
||||
" sum(obj_int_vars[i] * obj_int_coeffs[i]\n",
|
||||
" for i in range(len(obj_int_vars))))\n",
|
||||
" sum(obj_bool_vars[i] * obj_bool_coeffs[i] for i in range(len(obj_bool_vars)))\n",
|
||||
" + sum(obj_int_vars[i] * obj_int_coeffs[i] for i in range(len(obj_int_vars)))\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" if output_proto:\n",
|
||||
" print('Writing proto to %s' % output_proto)\n",
|
||||
" with open(output_proto, 'w') as text_file:\n",
|
||||
" print(\"Writing proto to %s\" % output_proto)\n",
|
||||
" with open(output_proto, \"w\") as text_file:\n",
|
||||
" text_file.write(str(model))\n",
|
||||
"\n",
|
||||
" # Solve the model.\n",
|
||||
@@ -450,38 +466,40 @@
|
||||
" # Print solution.\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" print()\n",
|
||||
" header = ' '\n",
|
||||
" header = \" \"\n",
|
||||
" for w in range(num_weeks):\n",
|
||||
" header += 'M T W T F S S '\n",
|
||||
" header += \"M T W T F S S \"\n",
|
||||
" print(header)\n",
|
||||
" for e in range(num_employees):\n",
|
||||
" schedule = ''\n",
|
||||
" schedule = \"\"\n",
|
||||
" for d in range(num_days):\n",
|
||||
" for s in range(num_shifts):\n",
|
||||
" if solver.BooleanValue(work[e, s, d]):\n",
|
||||
" schedule += shifts[s] + ' '\n",
|
||||
" print('worker %i: %s' % (e, schedule))\n",
|
||||
" schedule += shifts[s] + \" \"\n",
|
||||
" print(\"worker %i: %s\" % (e, schedule))\n",
|
||||
" print()\n",
|
||||
" print('Penalties:')\n",
|
||||
" print(\"Penalties:\")\n",
|
||||
" for i, var in enumerate(obj_bool_vars):\n",
|
||||
" if solver.BooleanValue(var):\n",
|
||||
" penalty = obj_bool_coeffs[i]\n",
|
||||
" if penalty > 0:\n",
|
||||
" print(' %s violated, penalty=%i' % (var.Name(), penalty))\n",
|
||||
" print(\" %s violated, penalty=%i\" % (var.Name(), penalty))\n",
|
||||
" else:\n",
|
||||
" print(' %s fulfilled, gain=%i' % (var.Name(), -penalty))\n",
|
||||
" print(\" %s fulfilled, gain=%i\" % (var.Name(), -penalty))\n",
|
||||
"\n",
|
||||
" for i, var in enumerate(obj_int_vars):\n",
|
||||
" if solver.Value(var) > 0:\n",
|
||||
" print(' %s violated by %i, linear penalty=%i' %\n",
|
||||
" (var.Name(), solver.Value(var), obj_int_coeffs[i]))\n",
|
||||
" print(\n",
|
||||
" \" %s violated by %i, linear penalty=%i\"\n",
|
||||
" % (var.Name(), solver.Value(var), obj_int_coeffs[i])\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" print()\n",
|
||||
" print('Statistics')\n",
|
||||
" print(' - status : %s' % solver.StatusName(status))\n",
|
||||
" print(' - conflicts : %i' % solver.NumConflicts())\n",
|
||||
" print(' - branches : %i' % solver.NumBranches())\n",
|
||||
" print(' - wall time : %f s' % solver.WallTime())\n",
|
||||
" print(\"Statistics\")\n",
|
||||
" print(\" - status : %s\" % solver.StatusName(status))\n",
|
||||
" print(\" - conflicts : %i\" % solver.NumConflicts())\n",
|
||||
" print(\" - branches : %i\" % solver.NumBranches())\n",
|
||||
" print(\" - wall time : %f s\" % solver.WallTime())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(_):\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Single machine jobshop with setup times, release dates and due dates.\n"
|
||||
]
|
||||
},
|
||||
@@ -86,19 +87,22 @@
|
||||
"from google.protobuf import text_format\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"#----------------------------------------------------------------------------\n",
|
||||
"# ----------------------------------------------------------------------------\n",
|
||||
"# Command line arguments.\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:16,log_search_progress:true,max_time_in_seconds:45',\n",
|
||||
" 'Sat solver parameters.')\n",
|
||||
"_PREPROCESS = flags.DEFINE_bool('--preprocess_times', True,\n",
|
||||
" 'Preprocess setup times and durations')\n",
|
||||
" \"params\",\n",
|
||||
" \"num_search_workers:16,log_search_progress:true,max_time_in_seconds:45\",\n",
|
||||
" \"Sat solver parameters.\",\n",
|
||||
")\n",
|
||||
"_PREPROCESS = flags.DEFINE_bool(\n",
|
||||
" \"--preprocess_times\", True, \"Preprocess setup times and durations\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"#----------------------------------------------------------------------------\n",
|
||||
"# ----------------------------------------------------------------------------\n",
|
||||
"# Intermediate solution printer\n",
|
||||
"class SolutionPrinter(cp_model.CpSolverSolutionCallback):\n",
|
||||
" \"\"\"Print intermediate solutions.\"\"\"\n",
|
||||
@@ -109,8 +113,10 @@
|
||||
"\n",
|
||||
" def on_solution_callback(self):\n",
|
||||
" \"\"\"Called after each new solution found.\"\"\"\n",
|
||||
" print('Solution %i, time = %f s, objective = %i' %\n",
|
||||
" (self.__solution_count, self.WallTime(), self.ObjectiveValue()))\n",
|
||||
" print(\n",
|
||||
" \"Solution %i, time = %f s, objective = %i\"\n",
|
||||
" % (self.__solution_count, self.WallTime(), self.ObjectiveValue())\n",
|
||||
" )\n",
|
||||
" self.__solution_count += 1\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -120,110 +126,343 @@
|
||||
" parameters = _PARAMS.value\n",
|
||||
" output_proto_file = _OUTPUT_PROTO.value\n",
|
||||
"\n",
|
||||
" #----------------------------------------------------------------------------\n",
|
||||
" # ----------------------------------------------------------------------------\n",
|
||||
" # Data.\n",
|
||||
"\n",
|
||||
" job_durations = [\n",
|
||||
" 2546, 8589, 5953, 3710, 3630, 3016, 4148, 8706, 1604, 5502, 9983, 6209,\n",
|
||||
" 9920, 7860, 2176\n",
|
||||
" 2546,\n",
|
||||
" 8589,\n",
|
||||
" 5953,\n",
|
||||
" 3710,\n",
|
||||
" 3630,\n",
|
||||
" 3016,\n",
|
||||
" 4148,\n",
|
||||
" 8706,\n",
|
||||
" 1604,\n",
|
||||
" 5502,\n",
|
||||
" 9983,\n",
|
||||
" 6209,\n",
|
||||
" 9920,\n",
|
||||
" 7860,\n",
|
||||
" 2176,\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" setup_times = [\n",
|
||||
" [\n",
|
||||
" 3559, 1638, 2000, 3676, 2741, 2439, 2406, 1526, 1600, 3356, 4324,\n",
|
||||
" 1923, 3663, 4103, 2215\n",
|
||||
" 3559,\n",
|
||||
" 1638,\n",
|
||||
" 2000,\n",
|
||||
" 3676,\n",
|
||||
" 2741,\n",
|
||||
" 2439,\n",
|
||||
" 2406,\n",
|
||||
" 1526,\n",
|
||||
" 1600,\n",
|
||||
" 3356,\n",
|
||||
" 4324,\n",
|
||||
" 1923,\n",
|
||||
" 3663,\n",
|
||||
" 4103,\n",
|
||||
" 2215,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 1442, 3010, 1641, 4490, 2060, 2143, 3376, 3891, 3513, 2855, 2653,\n",
|
||||
" 1471, 2257, 1186, 2354\n",
|
||||
" 1442,\n",
|
||||
" 3010,\n",
|
||||
" 1641,\n",
|
||||
" 4490,\n",
|
||||
" 2060,\n",
|
||||
" 2143,\n",
|
||||
" 3376,\n",
|
||||
" 3891,\n",
|
||||
" 3513,\n",
|
||||
" 2855,\n",
|
||||
" 2653,\n",
|
||||
" 1471,\n",
|
||||
" 2257,\n",
|
||||
" 1186,\n",
|
||||
" 2354,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 1728, 3583, 3243, 4080, 2191, 3644, 4023, 3510, 2135, 1346, 1410,\n",
|
||||
" 3565, 3181, 1126, 4169\n",
|
||||
" 1728,\n",
|
||||
" 3583,\n",
|
||||
" 3243,\n",
|
||||
" 4080,\n",
|
||||
" 2191,\n",
|
||||
" 3644,\n",
|
||||
" 4023,\n",
|
||||
" 3510,\n",
|
||||
" 2135,\n",
|
||||
" 1346,\n",
|
||||
" 1410,\n",
|
||||
" 3565,\n",
|
||||
" 3181,\n",
|
||||
" 1126,\n",
|
||||
" 4169,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 1291, 1703, 3103, 4001, 1712, 1137, 3341, 3485, 2557, 2435, 1972,\n",
|
||||
" 1986, 1522, 4734, 2520\n",
|
||||
" 1291,\n",
|
||||
" 1703,\n",
|
||||
" 3103,\n",
|
||||
" 4001,\n",
|
||||
" 1712,\n",
|
||||
" 1137,\n",
|
||||
" 3341,\n",
|
||||
" 3485,\n",
|
||||
" 2557,\n",
|
||||
" 2435,\n",
|
||||
" 1972,\n",
|
||||
" 1986,\n",
|
||||
" 1522,\n",
|
||||
" 4734,\n",
|
||||
" 2520,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 4134, 2200, 1502, 3995, 1277, 1808, 1020, 2078, 2999, 1605, 1697,\n",
|
||||
" 2323, 2268, 2288, 4856\n",
|
||||
" 4134,\n",
|
||||
" 2200,\n",
|
||||
" 1502,\n",
|
||||
" 3995,\n",
|
||||
" 1277,\n",
|
||||
" 1808,\n",
|
||||
" 1020,\n",
|
||||
" 2078,\n",
|
||||
" 2999,\n",
|
||||
" 1605,\n",
|
||||
" 1697,\n",
|
||||
" 2323,\n",
|
||||
" 2268,\n",
|
||||
" 2288,\n",
|
||||
" 4856,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 4974, 2480, 2492, 4088, 2587, 4652, 1478, 3942, 1222, 3305, 1206,\n",
|
||||
" 1024, 2605, 3080, 3516\n",
|
||||
" 4974,\n",
|
||||
" 2480,\n",
|
||||
" 2492,\n",
|
||||
" 4088,\n",
|
||||
" 2587,\n",
|
||||
" 4652,\n",
|
||||
" 1478,\n",
|
||||
" 3942,\n",
|
||||
" 1222,\n",
|
||||
" 3305,\n",
|
||||
" 1206,\n",
|
||||
" 1024,\n",
|
||||
" 2605,\n",
|
||||
" 3080,\n",
|
||||
" 3516,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 1903, 2584, 2104, 1609, 4745, 2691, 1539, 2544, 2499, 2074, 4793,\n",
|
||||
" 1756, 2190, 1298, 2605\n",
|
||||
" 1903,\n",
|
||||
" 2584,\n",
|
||||
" 2104,\n",
|
||||
" 1609,\n",
|
||||
" 4745,\n",
|
||||
" 2691,\n",
|
||||
" 1539,\n",
|
||||
" 2544,\n",
|
||||
" 2499,\n",
|
||||
" 2074,\n",
|
||||
" 4793,\n",
|
||||
" 1756,\n",
|
||||
" 2190,\n",
|
||||
" 1298,\n",
|
||||
" 2605,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 1407, 2536, 2296, 1769, 1449, 3386, 3046, 1180, 4132, 4783, 3386,\n",
|
||||
" 3429, 2450, 3376, 3719\n",
|
||||
" 1407,\n",
|
||||
" 2536,\n",
|
||||
" 2296,\n",
|
||||
" 1769,\n",
|
||||
" 1449,\n",
|
||||
" 3386,\n",
|
||||
" 3046,\n",
|
||||
" 1180,\n",
|
||||
" 4132,\n",
|
||||
" 4783,\n",
|
||||
" 3386,\n",
|
||||
" 3429,\n",
|
||||
" 2450,\n",
|
||||
" 3376,\n",
|
||||
" 3719,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 3026, 1637, 3628, 3096, 1498, 4947, 1912, 3703, 4107, 4730, 1805,\n",
|
||||
" 2189, 1789, 1985, 3586\n",
|
||||
" 3026,\n",
|
||||
" 1637,\n",
|
||||
" 3628,\n",
|
||||
" 3096,\n",
|
||||
" 1498,\n",
|
||||
" 4947,\n",
|
||||
" 1912,\n",
|
||||
" 3703,\n",
|
||||
" 4107,\n",
|
||||
" 4730,\n",
|
||||
" 1805,\n",
|
||||
" 2189,\n",
|
||||
" 1789,\n",
|
||||
" 1985,\n",
|
||||
" 3586,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 3940, 1342, 1601, 2737, 1748, 3771, 4052, 1619, 2558, 3782, 4383,\n",
|
||||
" 3451, 4904, 1108, 1750\n",
|
||||
" 3940,\n",
|
||||
" 1342,\n",
|
||||
" 1601,\n",
|
||||
" 2737,\n",
|
||||
" 1748,\n",
|
||||
" 3771,\n",
|
||||
" 4052,\n",
|
||||
" 1619,\n",
|
||||
" 2558,\n",
|
||||
" 3782,\n",
|
||||
" 4383,\n",
|
||||
" 3451,\n",
|
||||
" 4904,\n",
|
||||
" 1108,\n",
|
||||
" 1750,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 1348, 3162, 1507, 3936, 1453, 2953, 4182, 2968, 3134, 1042, 3175,\n",
|
||||
" 2805, 4901, 1735, 1654\n",
|
||||
" 1348,\n",
|
||||
" 3162,\n",
|
||||
" 1507,\n",
|
||||
" 3936,\n",
|
||||
" 1453,\n",
|
||||
" 2953,\n",
|
||||
" 4182,\n",
|
||||
" 2968,\n",
|
||||
" 3134,\n",
|
||||
" 1042,\n",
|
||||
" 3175,\n",
|
||||
" 2805,\n",
|
||||
" 4901,\n",
|
||||
" 1735,\n",
|
||||
" 1654,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 1099, 1711, 1245, 1067, 4343, 3407, 1108, 1784, 4803, 2342, 3377,\n",
|
||||
" 2037, 3563, 1621, 2840\n",
|
||||
" 1099,\n",
|
||||
" 1711,\n",
|
||||
" 1245,\n",
|
||||
" 1067,\n",
|
||||
" 4343,\n",
|
||||
" 3407,\n",
|
||||
" 1108,\n",
|
||||
" 1784,\n",
|
||||
" 4803,\n",
|
||||
" 2342,\n",
|
||||
" 3377,\n",
|
||||
" 2037,\n",
|
||||
" 3563,\n",
|
||||
" 1621,\n",
|
||||
" 2840,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 2573, 4222, 3164, 2563, 3231, 4731, 2395, 1033, 4795, 3288, 2335,\n",
|
||||
" 4935, 4066, 1440, 4979\n",
|
||||
" 2573,\n",
|
||||
" 4222,\n",
|
||||
" 3164,\n",
|
||||
" 2563,\n",
|
||||
" 3231,\n",
|
||||
" 4731,\n",
|
||||
" 2395,\n",
|
||||
" 1033,\n",
|
||||
" 4795,\n",
|
||||
" 3288,\n",
|
||||
" 2335,\n",
|
||||
" 4935,\n",
|
||||
" 4066,\n",
|
||||
" 1440,\n",
|
||||
" 4979,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 3321, 1666, 3573, 2377, 4649, 4600, 1065, 2475, 3658, 3374, 1138,\n",
|
||||
" 4367, 4728, 3032, 2198\n",
|
||||
" 3321,\n",
|
||||
" 1666,\n",
|
||||
" 3573,\n",
|
||||
" 2377,\n",
|
||||
" 4649,\n",
|
||||
" 4600,\n",
|
||||
" 1065,\n",
|
||||
" 2475,\n",
|
||||
" 3658,\n",
|
||||
" 3374,\n",
|
||||
" 1138,\n",
|
||||
" 4367,\n",
|
||||
" 4728,\n",
|
||||
" 3032,\n",
|
||||
" 2198,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 2986, 1180, 4095, 3132, 3987, 3880, 3526, 1460, 4885, 3827, 4945,\n",
|
||||
" 4419, 3486, 3805, 3804\n",
|
||||
" 2986,\n",
|
||||
" 1180,\n",
|
||||
" 4095,\n",
|
||||
" 3132,\n",
|
||||
" 3987,\n",
|
||||
" 3880,\n",
|
||||
" 3526,\n",
|
||||
" 1460,\n",
|
||||
" 4885,\n",
|
||||
" 3827,\n",
|
||||
" 4945,\n",
|
||||
" 4419,\n",
|
||||
" 3486,\n",
|
||||
" 3805,\n",
|
||||
" 3804,\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 4163, 3441, 1217, 2941, 1210, 3794, 1779, 1904, 4255, 4967, 4003,\n",
|
||||
" 3873, 1002, 2055, 4295\n",
|
||||
" 4163,\n",
|
||||
" 3441,\n",
|
||||
" 1217,\n",
|
||||
" 2941,\n",
|
||||
" 1210,\n",
|
||||
" 3794,\n",
|
||||
" 1779,\n",
|
||||
" 1904,\n",
|
||||
" 4255,\n",
|
||||
" 4967,\n",
|
||||
" 4003,\n",
|
||||
" 3873,\n",
|
||||
" 1002,\n",
|
||||
" 2055,\n",
|
||||
" 4295,\n",
|
||||
" ],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" due_dates = [\n",
|
||||
" -1, -1, 28569, -1, 98104, 27644, 55274, 57364, -1, -1, 60875, 96637,\n",
|
||||
" 77888, -1, -1\n",
|
||||
" ]\n",
|
||||
" release_dates = [\n",
|
||||
" 0, 0, 0, 0, 19380, 0, 0, 48657, 0, 27932, 0, 0, 24876, 0, 0\n",
|
||||
" -1,\n",
|
||||
" -1,\n",
|
||||
" 28569,\n",
|
||||
" -1,\n",
|
||||
" 98104,\n",
|
||||
" 27644,\n",
|
||||
" 55274,\n",
|
||||
" 57364,\n",
|
||||
" -1,\n",
|
||||
" -1,\n",
|
||||
" 60875,\n",
|
||||
" 96637,\n",
|
||||
" 77888,\n",
|
||||
" -1,\n",
|
||||
" -1,\n",
|
||||
" ]\n",
|
||||
" release_dates = [0, 0, 0, 0, 19380, 0, 0, 48657, 0, 27932, 0, 0, 24876, 0, 0]\n",
|
||||
"\n",
|
||||
" precedences = [(0, 2), (1, 2)]\n",
|
||||
"\n",
|
||||
" #----------------------------------------------------------------------------\n",
|
||||
" # ----------------------------------------------------------------------------\n",
|
||||
" # Helper data.\n",
|
||||
" num_jobs = len(job_durations)\n",
|
||||
" all_jobs = range(num_jobs)\n",
|
||||
"\n",
|
||||
" #----------------------------------------------------------------------------\n",
|
||||
" # ----------------------------------------------------------------------------\n",
|
||||
" # Preprocess.\n",
|
||||
" if _PREPROCESS.value:\n",
|
||||
" for job_id in all_jobs:\n",
|
||||
" min_incoming_setup = min(\n",
|
||||
" setup_times[j][job_id] for j in range(num_jobs + 1))\n",
|
||||
" setup_times[j][job_id] for j in range(num_jobs + 1)\n",
|
||||
" )\n",
|
||||
" if release_dates[job_id] != 0:\n",
|
||||
" min_incoming_setup = min(min_incoming_setup,\n",
|
||||
" release_dates[job_id])\n",
|
||||
" min_incoming_setup = min(min_incoming_setup, release_dates[job_id])\n",
|
||||
" if min_incoming_setup == 0:\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" print('job %i has a min incoming setup of %i' %\n",
|
||||
" (job_id, min_incoming_setup))\n",
|
||||
" print(\n",
|
||||
" \"job %i has a min incoming setup of %i\" % (job_id, min_incoming_setup)\n",
|
||||
" )\n",
|
||||
" # We can transfer some setup times to the duration of the job.\n",
|
||||
" job_durations[job_id] += min_incoming_setup\n",
|
||||
" # Decrease corresponding incoming setup times.\n",
|
||||
@@ -233,36 +472,37 @@
|
||||
" if release_dates[job_id] != 0:\n",
|
||||
" release_dates[job_id] -= min_incoming_setup\n",
|
||||
"\n",
|
||||
" #----------------------------------------------------------------------------\n",
|
||||
" # ----------------------------------------------------------------------------\n",
|
||||
" # Model.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
" #----------------------------------------------------------------------------\n",
|
||||
" # ----------------------------------------------------------------------------\n",
|
||||
" # Compute a maximum makespan greedily.\n",
|
||||
" horizon = sum(job_durations) + sum(\n",
|
||||
" max(setup_times[i][j]\n",
|
||||
" for i in range(num_jobs + 1))\n",
|
||||
" for j in range(num_jobs))\n",
|
||||
" print('Greedy horizon =', horizon)\n",
|
||||
" max(setup_times[i][j] for i in range(num_jobs + 1)) for j in range(num_jobs)\n",
|
||||
" )\n",
|
||||
" print(\"Greedy horizon =\", horizon)\n",
|
||||
"\n",
|
||||
" #----------------------------------------------------------------------------\n",
|
||||
" # ----------------------------------------------------------------------------\n",
|
||||
" # Global storage of variables.\n",
|
||||
" intervals = []\n",
|
||||
" starts = []\n",
|
||||
" ends = []\n",
|
||||
"\n",
|
||||
" #----------------------------------------------------------------------------\n",
|
||||
" # ----------------------------------------------------------------------------\n",
|
||||
" # Scan the jobs and create the relevant variables and intervals.\n",
|
||||
" for job_id in all_jobs:\n",
|
||||
" duration = job_durations[job_id]\n",
|
||||
" release_date = release_dates[job_id]\n",
|
||||
" due_date = due_dates[job_id] if due_dates[job_id] != -1 else horizon\n",
|
||||
" print('job %2i: start = %5i, duration = %4i, end = %6i' %\n",
|
||||
" (job_id, release_date, duration, due_date))\n",
|
||||
" name_suffix = '_%i' % job_id\n",
|
||||
" start = model.NewIntVar(release_date, due_date, 's' + name_suffix)\n",
|
||||
" end = model.NewIntVar(release_date, due_date, 'e' + name_suffix)\n",
|
||||
" interval = model.NewIntervalVar(start, duration, end, 'i' + name_suffix)\n",
|
||||
" print(\n",
|
||||
" \"job %2i: start = %5i, duration = %4i, end = %6i\"\n",
|
||||
" % (job_id, release_date, duration, due_date)\n",
|
||||
" )\n",
|
||||
" name_suffix = \"_%i\" % job_id\n",
|
||||
" start = model.NewIntVar(release_date, due_date, \"s\" + name_suffix)\n",
|
||||
" end = model.NewIntVar(release_date, due_date, \"e\" + name_suffix)\n",
|
||||
" interval = model.NewIntervalVar(start, duration, end, \"i\" + name_suffix)\n",
|
||||
" starts.append(start)\n",
|
||||
" ends.append(end)\n",
|
||||
" intervals.append(interval)\n",
|
||||
@@ -270,59 +510,61 @@
|
||||
" # No overlap constraint.\n",
|
||||
" model.AddNoOverlap(intervals)\n",
|
||||
"\n",
|
||||
" #----------------------------------------------------------------------------\n",
|
||||
" # ----------------------------------------------------------------------------\n",
|
||||
" # Transition times using a circuit constraint.\n",
|
||||
" arcs = []\n",
|
||||
" for i in all_jobs:\n",
|
||||
" # Initial arc from the dummy node (0) to a task.\n",
|
||||
" start_lit = model.NewBoolVar('')\n",
|
||||
" arcs.append([0, i + 1, start_lit])\n",
|
||||
" start_lit = model.NewBoolVar(\"\")\n",
|
||||
" arcs.append((0, i + 1, start_lit))\n",
|
||||
" # If this task is the first, set to minimum starting time.\n",
|
||||
" min_start_time = max(release_dates[i], setup_times[0][i])\n",
|
||||
" model.Add(starts[i] == min_start_time).OnlyEnforceIf(start_lit)\n",
|
||||
" # Final arc from an arc to the dummy node.\n",
|
||||
" arcs.append([i + 1, 0, model.NewBoolVar('')])\n",
|
||||
" arcs.append((i + 1, 0, model.NewBoolVar(\"\")))\n",
|
||||
"\n",
|
||||
" for j in all_jobs:\n",
|
||||
" if i == j:\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" lit = model.NewBoolVar('%i follows %i' % (j, i))\n",
|
||||
" arcs.append([i + 1, j + 1, lit])\n",
|
||||
" lit = model.NewBoolVar(\"%i follows %i\" % (j, i))\n",
|
||||
" arcs.append((i + 1, j + 1, lit))\n",
|
||||
"\n",
|
||||
" # We add the reified precedence to link the literal with the times of the\n",
|
||||
" # two tasks.\n",
|
||||
" # If release_dates[j] == 0, we can strenghten this precedence into an\n",
|
||||
" # equality as we are minimizing the makespan.\n",
|
||||
" if release_dates[j] == 0:\n",
|
||||
" model.Add(starts[j] == ends[i] +\n",
|
||||
" setup_times[i + 1][j]).OnlyEnforceIf(lit)\n",
|
||||
" model.Add(starts[j] == ends[i] + setup_times[i + 1][j]).OnlyEnforceIf(\n",
|
||||
" lit\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" model.Add(starts[j] >= ends[i] +\n",
|
||||
" setup_times[i + 1][j]).OnlyEnforceIf(lit)\n",
|
||||
" model.Add(starts[j] >= ends[i] + setup_times[i + 1][j]).OnlyEnforceIf(\n",
|
||||
" lit\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" model.AddCircuit(arcs)\n",
|
||||
"\n",
|
||||
" #----------------------------------------------------------------------------\n",
|
||||
" # ----------------------------------------------------------------------------\n",
|
||||
" # Precedences.\n",
|
||||
" for before, after in precedences:\n",
|
||||
" print('job %i is after job %i' % (after, before))\n",
|
||||
" print(\"job %i is after job %i\" % (after, before))\n",
|
||||
" model.Add(ends[before] <= starts[after])\n",
|
||||
"\n",
|
||||
" #----------------------------------------------------------------------------\n",
|
||||
" # ----------------------------------------------------------------------------\n",
|
||||
" # Objective.\n",
|
||||
" makespan = model.NewIntVar(0, horizon, 'makespan')\n",
|
||||
" makespan = model.NewIntVar(0, horizon, \"makespan\")\n",
|
||||
" model.AddMaxEquality(makespan, ends)\n",
|
||||
" model.Minimize(makespan)\n",
|
||||
"\n",
|
||||
" #----------------------------------------------------------------------------\n",
|
||||
" # ----------------------------------------------------------------------------\n",
|
||||
" # Write problem to file.\n",
|
||||
" if output_proto_file:\n",
|
||||
" print('Writing proto to %s' % output_proto_file)\n",
|
||||
" with open(output_proto_file, 'w') as text_file:\n",
|
||||
" print(\"Writing proto to %s\" % output_proto_file)\n",
|
||||
" with open(output_proto_file, \"w\") as text_file:\n",
|
||||
" text_file.write(str(model))\n",
|
||||
"\n",
|
||||
" #----------------------------------------------------------------------------\n",
|
||||
" # ----------------------------------------------------------------------------\n",
|
||||
" # Solve.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" if parameters:\n",
|
||||
@@ -331,13 +573,14 @@
|
||||
" solver.Solve(model, solution_printer)\n",
|
||||
" for job_id in all_jobs:\n",
|
||||
" print(\n",
|
||||
" 'job %i starts at %i end ends at %i' %\n",
|
||||
" (job_id, solver.Value(starts[job_id]), solver.Value(ends[job_id])))\n",
|
||||
" \"job %i starts at %i end ends at %i\"\n",
|
||||
" % (job_id, solver.Value(starts[job_id]), solver.Value(ends[job_id]))\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" single_machine_scheduling()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Maximize the minimum of pairwise distances between n robots in a square space.\n"
|
||||
]
|
||||
},
|
||||
@@ -87,14 +88,14 @@
|
||||
"from google.protobuf import text_format\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"_NUM_ROBOTS = flags.DEFINE_integer('num_robots', 8,\n",
|
||||
" 'Number of robots to place.')\n",
|
||||
"_ROOM_SIZE = flags.DEFINE_integer('room_size', 20,\n",
|
||||
" 'Size of the square room where robots are.')\n",
|
||||
"_NUM_ROBOTS = flags.DEFINE_integer(\"num_robots\", 8, \"Number of robots to place.\")\n",
|
||||
"_ROOM_SIZE = flags.DEFINE_integer(\n",
|
||||
" \"room_size\", 20, \"Size of the square room where robots are.\"\n",
|
||||
")\n",
|
||||
"_PARAMS = flags.DEFINE_string(\n",
|
||||
" 'params',\n",
|
||||
" 'num_search_workers:16, max_time_in_seconds:20',\n",
|
||||
" 'Sat solver parameters.',\n",
|
||||
" \"params\",\n",
|
||||
" \"num_search_workers:16, max_time_in_seconds:20\",\n",
|
||||
" \"Sat solver parameters.\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -103,8 +104,8 @@
|
||||
" model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
" # Create the list of coordinates (x, y) for each robot.\n",
|
||||
" x = [model.NewIntVar(1, room_size, f'x_{i}') for i in range(num_robots)]\n",
|
||||
" y = [model.NewIntVar(1, room_size, f'y_{i}') for i in range(num_robots)]\n",
|
||||
" x = [model.NewIntVar(1, room_size, f\"x_{i}\") for i in range(num_robots)]\n",
|
||||
" y = [model.NewIntVar(1, room_size, f\"y_{i}\") for i in range(num_robots)]\n",
|
||||
"\n",
|
||||
" # The specification of the problem is to maximize the minimum euclidian\n",
|
||||
" # distance between any two robots. Unfortunately, the euclidian distance\n",
|
||||
@@ -123,30 +124,30 @@
|
||||
" # forall i:\n",
|
||||
" # scaled_min_square_distance <= scaling * (x_diff_sq[i] + y_diff_sq[i])\n",
|
||||
" scaling = 1000\n",
|
||||
" scaled_min_square_distance = model.NewIntVar(0, 2 * scaling * room_size**2,\n",
|
||||
" 'scaled_min_square_distance')\n",
|
||||
" scaled_min_square_distance = model.NewIntVar(\n",
|
||||
" 0, 2 * scaling * room_size**2, \"scaled_min_square_distance\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Build intermediate variables and get the list of squared distances on\n",
|
||||
" # each dimension.\n",
|
||||
" for i in range(num_robots - 1):\n",
|
||||
" for j in range(i + 1, num_robots):\n",
|
||||
" # Compute the distance on each dimension between robot i and robot j.\n",
|
||||
" x_diff = model.NewIntVar(-room_size, room_size, f'x_diff{i}')\n",
|
||||
" y_diff = model.NewIntVar(-room_size, room_size, f'y_diff{i}')\n",
|
||||
" x_diff = model.NewIntVar(-room_size, room_size, f\"x_diff{i}\")\n",
|
||||
" y_diff = model.NewIntVar(-room_size, room_size, f\"y_diff{i}\")\n",
|
||||
" model.Add(x_diff == x[i] - x[j])\n",
|
||||
" model.Add(y_diff == y[i] - y[j])\n",
|
||||
"\n",
|
||||
" # Compute the square of the previous differences.\n",
|
||||
" x_diff_sq = model.NewIntVar(0, room_size**2, f'x_diff_sq{i}')\n",
|
||||
" y_diff_sq = model.NewIntVar(0, room_size**2, f'y_diff_sq{i}')\n",
|
||||
" x_diff_sq = model.NewIntVar(0, room_size**2, f\"x_diff_sq{i}\")\n",
|
||||
" y_diff_sq = model.NewIntVar(0, room_size**2, f\"y_diff_sq{i}\")\n",
|
||||
" model.AddMultiplicationEquality(x_diff_sq, x_diff, x_diff)\n",
|
||||
" model.AddMultiplicationEquality(y_diff_sq, y_diff, y_diff)\n",
|
||||
"\n",
|
||||
" # We just need to be <= to the scaled square distance as we are\n",
|
||||
" # maximizing the min distance, which is equivalent as maximizing the min\n",
|
||||
" # square distance.\n",
|
||||
" model.Add(scaled_min_square_distance <= scaling *\n",
|
||||
" (x_diff_sq + y_diff_sq))\n",
|
||||
" model.Add(scaled_min_square_distance <= scaling * (x_diff_sq + y_diff_sq))\n",
|
||||
"\n",
|
||||
" # Naive symmetry breaking.\n",
|
||||
" for i in range(1, num_robots):\n",
|
||||
@@ -165,17 +166,19 @@
|
||||
"\n",
|
||||
" # Prints the solution.\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" print(f'Spread {num_robots} with a min pairwise distance of'\n",
|
||||
" f' {math.sqrt(solver.ObjectiveValue() / scaling)}')\n",
|
||||
" print(\n",
|
||||
" f\"Spread {num_robots} with a min pairwise distance of\"\n",
|
||||
" f\" {math.sqrt(solver.ObjectiveValue() / scaling)}\"\n",
|
||||
" )\n",
|
||||
" for i in range(num_robots):\n",
|
||||
" print(f'robot {i}: x={solver.Value(x[i])} y={solver.Value(y[i])}')\n",
|
||||
" print(f\"robot {i}: x={solver.Value(x[i])} y={solver.Value(y[i])}\")\n",
|
||||
" else:\n",
|
||||
" print('No solution found.')\n",
|
||||
" print(\"No solution found.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
"\n",
|
||||
" spread_robots(_NUM_ROBOTS.value, _ROOM_SIZE.value, _PARAMS.value)\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solves the Stell Mill Slab problem with 4 different techniques.\n",
|
||||
"\n"
|
||||
]
|
||||
@@ -86,230 +87,94 @@
|
||||
"import collections\n",
|
||||
"import time\n",
|
||||
"\n",
|
||||
"from google.protobuf import text_format\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"_PROBLEM = flags.DEFINE_integer('problem', 2, 'Problem id to solve.')\n",
|
||||
"\n",
|
||||
"_PROBLEM = flags.DEFINE_integer(\"problem\", 2, \"Problem id to solve.\")\n",
|
||||
"_BREAK_SYMMETRIES = flags.DEFINE_boolean(\n",
|
||||
" 'break_symmetries', True, 'Break symmetries between equivalent orders.')\n",
|
||||
" \"break_symmetries\", True, \"Break symmetries between equivalent orders.\"\n",
|
||||
")\n",
|
||||
"_SOLVER = flags.DEFINE_string(\n",
|
||||
" 'solver', 'sat_column', 'Method used to solve: sat, sat_table, sat_column.')\n",
|
||||
" \"solver\", \"sat_column\", \"Method used to solve: sat, sat_table, sat_column.\"\n",
|
||||
")\n",
|
||||
"_PARAMS = flags.DEFINE_string(\n",
|
||||
" \"params\",\n",
|
||||
" \"max_time_in_seconds:20,num_workers:8,log_search_progress:true\",\n",
|
||||
" \"CP-SAT parameters.\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def build_problem(problem_id):\n",
|
||||
" \"\"\"Build problem data.\"\"\"\n",
|
||||
" capacities = None\n",
|
||||
" num_colors = None\n",
|
||||
" num_slabs = None\n",
|
||||
" orders = None\n",
|
||||
"\n",
|
||||
" if problem_id == 0:\n",
|
||||
" capacities = [\n",
|
||||
" 0, 12, 14, 17, 18, 19, 20, 23, 24, 25, 26, 27, 28, 29, 30, 32, 35,\n",
|
||||
" 39, 42, 43, 44\n",
|
||||
" # fmt:off\n",
|
||||
" 0, 12, 14, 17, 18, 19, 20, 23, 24, 25, 26, 27, 28, 29, 30, 32, 35, 39, 42, 43, 44,\n",
|
||||
" # fmt:on\n",
|
||||
" ]\n",
|
||||
" num_colors = 88\n",
|
||||
" num_slabs = 111\n",
|
||||
" orders = [\n",
|
||||
" (4, 1), # (size, color)\n",
|
||||
" (22, 2),\n",
|
||||
" (9, 3),\n",
|
||||
" (5, 4),\n",
|
||||
" (8, 5),\n",
|
||||
" (3, 6),\n",
|
||||
" (3, 4),\n",
|
||||
" (4, 7),\n",
|
||||
" (7, 4),\n",
|
||||
" (7, 8),\n",
|
||||
" (3, 6),\n",
|
||||
" (2, 6),\n",
|
||||
" (2, 4),\n",
|
||||
" (8, 9),\n",
|
||||
" (5, 10),\n",
|
||||
" (7, 11),\n",
|
||||
" (4, 7),\n",
|
||||
" (7, 11),\n",
|
||||
" (5, 10),\n",
|
||||
" (7, 11),\n",
|
||||
" (8, 9),\n",
|
||||
" (3, 1),\n",
|
||||
" (25, 12),\n",
|
||||
" (14, 13),\n",
|
||||
" (3, 6),\n",
|
||||
" (22, 14),\n",
|
||||
" (19, 15),\n",
|
||||
" (19, 15),\n",
|
||||
" (22, 16),\n",
|
||||
" (22, 17),\n",
|
||||
" (22, 18),\n",
|
||||
" (20, 19),\n",
|
||||
" (22, 20),\n",
|
||||
" (5, 21),\n",
|
||||
" (4, 22),\n",
|
||||
" (10, 23),\n",
|
||||
" (26, 24),\n",
|
||||
" (17, 25),\n",
|
||||
" (20, 26),\n",
|
||||
" (16, 27),\n",
|
||||
" (10, 28),\n",
|
||||
" (19, 29),\n",
|
||||
" (10, 30),\n",
|
||||
" (10, 31),\n",
|
||||
" (23, 32),\n",
|
||||
" (22, 33),\n",
|
||||
" (26, 34),\n",
|
||||
" (27, 35),\n",
|
||||
" (22, 36),\n",
|
||||
" (27, 37),\n",
|
||||
" (22, 38),\n",
|
||||
" (22, 39),\n",
|
||||
" (13, 40),\n",
|
||||
" (14, 41),\n",
|
||||
" (16, 27),\n",
|
||||
" (26, 34),\n",
|
||||
" (26, 42),\n",
|
||||
" (27, 35),\n",
|
||||
" (22, 36),\n",
|
||||
" (20, 43),\n",
|
||||
" (26, 24),\n",
|
||||
" (22, 44),\n",
|
||||
" (13, 45),\n",
|
||||
" (19, 46),\n",
|
||||
" (20, 47),\n",
|
||||
" (16, 48),\n",
|
||||
" (15, 49),\n",
|
||||
" (17, 50),\n",
|
||||
" (10, 28),\n",
|
||||
" (20, 51),\n",
|
||||
" (5, 52),\n",
|
||||
" (26, 24),\n",
|
||||
" (19, 53),\n",
|
||||
" (15, 54),\n",
|
||||
" (10, 55),\n",
|
||||
" (10, 56),\n",
|
||||
" (13, 57),\n",
|
||||
" (13, 58),\n",
|
||||
" (13, 59),\n",
|
||||
" (12, 60),\n",
|
||||
" (12, 61),\n",
|
||||
" (18, 62),\n",
|
||||
" (10, 63),\n",
|
||||
" (18, 64),\n",
|
||||
" (16, 65),\n",
|
||||
" (20, 66),\n",
|
||||
" (12, 67),\n",
|
||||
" (6, 68),\n",
|
||||
" (6, 68),\n",
|
||||
" (15, 69),\n",
|
||||
" (15, 70),\n",
|
||||
" (15, 70),\n",
|
||||
" (21, 71),\n",
|
||||
" (30, 72),\n",
|
||||
" (30, 73),\n",
|
||||
" (30, 74),\n",
|
||||
" (30, 75),\n",
|
||||
" (23, 76),\n",
|
||||
" (15, 77),\n",
|
||||
" (15, 78),\n",
|
||||
" (27, 79),\n",
|
||||
" (27, 80),\n",
|
||||
" (27, 81),\n",
|
||||
" (27, 82),\n",
|
||||
" (27, 83),\n",
|
||||
" (27, 84),\n",
|
||||
" (27, 79),\n",
|
||||
" (27, 85),\n",
|
||||
" (27, 86),\n",
|
||||
" (10, 87),\n",
|
||||
" (3, 88)\n",
|
||||
" orders = [ # (size, color)\n",
|
||||
" # fmt:off\n",
|
||||
" (4, 1), (22, 2), (9, 3), (5, 4), (8, 5), (3, 6), (3, 4), (4, 7),\n",
|
||||
" (7, 4), (7, 8), (3, 6), (2, 6), (2, 4), (8, 9), (5, 10), (7, 11),\n",
|
||||
" (4, 7), (7, 11), (5, 10), (7, 11), (8, 9), (3, 1), (25, 12), (14, 13),\n",
|
||||
" (3, 6), (22, 14), (19, 15), (19, 15), (22, 16), (22, 17), (22, 18),\n",
|
||||
" (20, 19), (22, 20), (5, 21), (4, 22), (10, 23), (26, 24), (17, 25),\n",
|
||||
" (20, 26), (16, 27), (10, 28), (19, 29), (10, 30), (10, 31), (23, 32),\n",
|
||||
" (22, 33), (26, 34), (27, 35), (22, 36), (27, 37), (22, 38), (22, 39),\n",
|
||||
" (13, 40), (14, 41), (16, 27), (26, 34), (26, 42), (27, 35), (22, 36),\n",
|
||||
" (20, 43), (26, 24), (22, 44), (13, 45), (19, 46), (20, 47), (16, 48),\n",
|
||||
" (15, 49), (17, 50), (10, 28), (20, 51), (5, 52), (26, 24), (19, 53),\n",
|
||||
" (15, 54), (10, 55), (10, 56), (13, 57), (13, 58), (13, 59), (12, 60),\n",
|
||||
" (12, 61), (18, 62), (10, 63), (18, 64), (16, 65), (20, 66), (12, 67),\n",
|
||||
" (6, 68), (6, 68), (15, 69), (15, 70), (15, 70), (21, 71), (30, 72),\n",
|
||||
" (30, 73), (30, 74), (30, 75), (23, 76), (15, 77), (15, 78), (27, 79),\n",
|
||||
" (27, 80), (27, 81), (27, 82), (27, 83), (27, 84), (27, 79), (27, 85),\n",
|
||||
" (27, 86), (10, 87), (3, 88),\n",
|
||||
" # fmt:on\n",
|
||||
" ]\n",
|
||||
" elif problem_id == 1:\n",
|
||||
" capacities = [0, 17, 44]\n",
|
||||
" num_colors = 23\n",
|
||||
" num_slabs = 30\n",
|
||||
" orders = [\n",
|
||||
" (4, 1), # (size, color)\n",
|
||||
" (22, 2),\n",
|
||||
" (9, 3),\n",
|
||||
" (5, 4),\n",
|
||||
" (8, 5),\n",
|
||||
" (3, 6),\n",
|
||||
" (3, 4),\n",
|
||||
" (4, 7),\n",
|
||||
" (7, 4),\n",
|
||||
" (7, 8),\n",
|
||||
" (3, 6),\n",
|
||||
" (2, 6),\n",
|
||||
" (2, 4),\n",
|
||||
" (8, 9),\n",
|
||||
" (5, 10),\n",
|
||||
" (7, 11),\n",
|
||||
" (4, 7),\n",
|
||||
" (7, 11),\n",
|
||||
" (5, 10),\n",
|
||||
" (7, 11),\n",
|
||||
" (8, 9),\n",
|
||||
" (3, 1),\n",
|
||||
" (25, 12),\n",
|
||||
" (14, 13),\n",
|
||||
" (3, 6),\n",
|
||||
" (22, 14),\n",
|
||||
" (19, 15),\n",
|
||||
" (19, 15),\n",
|
||||
" (22, 16),\n",
|
||||
" (22, 17),\n",
|
||||
" (22, 18),\n",
|
||||
" (20, 19),\n",
|
||||
" (22, 20),\n",
|
||||
" (5, 21),\n",
|
||||
" (4, 22),\n",
|
||||
" (10, 23)\n",
|
||||
" orders = [ # (size, color)\n",
|
||||
" # fmt:off\n",
|
||||
" (4, 1), (22, 2), (9, 3), (5, 4), (8, 5), (3, 6), (3, 4), (4, 7), (7, 4),\n",
|
||||
" (7, 8), (3, 6), (2, 6), (2, 4), (8, 9), (5, 10), (7, 11), (4, 7), (7, 11),\n",
|
||||
" (5, 10), (7, 11), (8, 9), (3, 1), (25, 12), (14, 13), (3, 6), (22, 14),\n",
|
||||
" (19, 15), (19, 15), (22, 16), (22, 17), (22, 18), (20, 19), (22, 20),\n",
|
||||
" (5, 21), (4, 22), (10, 23),\n",
|
||||
" # fmt:on\n",
|
||||
" ]\n",
|
||||
" elif problem_id == 2:\n",
|
||||
" capacities = [0, 17, 44]\n",
|
||||
" num_colors = 15\n",
|
||||
" num_slabs = 20\n",
|
||||
" orders = [\n",
|
||||
" (4, 1), # (size, color)\n",
|
||||
" (22, 2),\n",
|
||||
" (9, 3),\n",
|
||||
" (5, 4),\n",
|
||||
" (8, 5),\n",
|
||||
" (3, 6),\n",
|
||||
" (3, 4),\n",
|
||||
" (4, 7),\n",
|
||||
" (7, 4),\n",
|
||||
" (7, 8),\n",
|
||||
" (3, 6),\n",
|
||||
" (2, 6),\n",
|
||||
" (2, 4),\n",
|
||||
" (8, 9),\n",
|
||||
" (5, 10),\n",
|
||||
" (7, 11),\n",
|
||||
" (4, 7),\n",
|
||||
" (7, 11),\n",
|
||||
" (5, 10),\n",
|
||||
" (7, 11),\n",
|
||||
" (8, 9),\n",
|
||||
" (3, 1),\n",
|
||||
" (25, 12),\n",
|
||||
" (14, 13),\n",
|
||||
" (3, 6),\n",
|
||||
" (22, 14),\n",
|
||||
" (19, 15),\n",
|
||||
" (19, 15)\n",
|
||||
" orders = [ # (size, color)\n",
|
||||
" # fmt:off\n",
|
||||
" (4, 1), (22, 2), (9, 3), (5, 4), (8, 5), (3, 6), (3, 4), (4, 7), (7, 4),\n",
|
||||
" (7, 8), (3, 6), (2, 6), (2, 4), (8, 9), (5, 10), (7, 11), (4, 7), (7, 11),\n",
|
||||
" (5, 10), (7, 11), (8, 9), (3, 1), (25, 12), (14, 13), (3, 6), (22, 14),\n",
|
||||
" (19, 15), (19, 15),\n",
|
||||
" # fmt:on\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" elif problem_id == 3:\n",
|
||||
" capacities = [0, 17, 44]\n",
|
||||
" num_colors = 8\n",
|
||||
" num_slabs = 10\n",
|
||||
" orders = [\n",
|
||||
" (4, 1), # (size, color)\n",
|
||||
" (22, 2),\n",
|
||||
" (9, 3),\n",
|
||||
" (5, 4),\n",
|
||||
" (8, 5),\n",
|
||||
" (3, 6),\n",
|
||||
" (3, 4),\n",
|
||||
" (4, 7),\n",
|
||||
" (7, 4),\n",
|
||||
" (7, 8),\n",
|
||||
" (3, 6)\n",
|
||||
" orders = [ # (size, color)\n",
|
||||
" # fmt:off\n",
|
||||
" (4, 1), (22, 2), (9, 3), (5, 4), (8, 5), (3, 6), (3, 4), (4, 7),\n",
|
||||
" (7, 4), (7, 8), (3, 6),\n",
|
||||
" # fmt:on\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" return (num_slabs, capacities, num_colors, orders)\n",
|
||||
@@ -333,21 +198,29 @@
|
||||
" \"\"\"Called on each new solution.\"\"\"\n",
|
||||
" current_time = time.time()\n",
|
||||
" objective = sum(self.Value(l) for l in self.__loss)\n",
|
||||
" print('Solution %i, time = %f s, objective = %i' %\n",
|
||||
" (self.__solution_count, current_time - self.__start_time,\n",
|
||||
" objective))\n",
|
||||
" print(\n",
|
||||
" \"Solution %i, time = %f s, objective = %i\"\n",
|
||||
" % (self.__solution_count, current_time - self.__start_time, objective)\n",
|
||||
" )\n",
|
||||
" self.__solution_count += 1\n",
|
||||
" orders_in_slab = [[\n",
|
||||
" o for o in self.__all_orders if self.Value(self.__assign[o][s])\n",
|
||||
" ] for s in self.__all_slabs]\n",
|
||||
" orders_in_slab = [\n",
|
||||
" [o for o in self.__all_orders if self.Value(self.__assign[o][s])]\n",
|
||||
" for s in self.__all_slabs\n",
|
||||
" ]\n",
|
||||
" for s in self.__all_slabs:\n",
|
||||
" if orders_in_slab[s]:\n",
|
||||
" line = ' - slab %i, load = %i, loss = %i, orders = [' % (\n",
|
||||
" s, self.Value(self.__load[s]), self.Value(self.__loss[s]))\n",
|
||||
" line = \" - slab %i, load = %i, loss = %i, orders = [\" % (\n",
|
||||
" s,\n",
|
||||
" self.Value(self.__load[s]),\n",
|
||||
" self.Value(self.__loss[s]),\n",
|
||||
" )\n",
|
||||
" for o in orders_in_slab[s]:\n",
|
||||
" line += '#%i(w%i, c%i) ' % (o, self.__orders[o][0],\n",
|
||||
" self.__orders[o][1])\n",
|
||||
" line += ']'\n",
|
||||
" line += \"#%i(w%i, c%i) \" % (\n",
|
||||
" o,\n",
|
||||
" self.__orders[o][0],\n",
|
||||
" self.__orders[o][1],\n",
|
||||
" )\n",
|
||||
" line += \"]\"\n",
|
||||
" print(line)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -361,16 +234,17 @@
|
||||
" all_slabs = range(num_slabs)\n",
|
||||
" all_colors = range(num_colors)\n",
|
||||
" all_orders = range(len(orders))\n",
|
||||
" print('Solving steel mill with %i orders, %i slabs, and %i capacities' %\n",
|
||||
" (num_orders, num_slabs, num_capacities - 1))\n",
|
||||
" print(\n",
|
||||
" \"Solving steel mill with %i orders, %i slabs, and %i capacities\"\n",
|
||||
" % (num_orders, num_slabs, num_capacities - 1)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Compute auxiliary data.\n",
|
||||
" widths = [x[0] for x in orders]\n",
|
||||
" colors = [x[1] for x in orders]\n",
|
||||
" max_capacity = max(capacities)\n",
|
||||
" loss_array = [\n",
|
||||
" min(x for x in capacities if x >= c) - c for c in range(max_capacity +\n",
|
||||
" 1)\n",
|
||||
" min(x for x in capacities if x >= c) - c for c in range(max_capacity + 1)\n",
|
||||
" ]\n",
|
||||
" max_loss = max(loss_array)\n",
|
||||
" orders_per_color = [\n",
|
||||
@@ -384,16 +258,15 @@
|
||||
"\n",
|
||||
" # Create the model and the decision variables.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
" assign = [[\n",
|
||||
" model.NewBoolVar('assign_%i_to_slab_%i' % (o, s)) for s in all_slabs\n",
|
||||
" ] for o in all_orders]\n",
|
||||
" loads = [\n",
|
||||
" model.NewIntVar(0, max_capacity, 'load_of_slab_%i' % s)\n",
|
||||
" assign = [\n",
|
||||
" [model.NewBoolVar(\"assign_%i_to_slab_%i\" % (o, s)) for s in all_slabs]\n",
|
||||
" for o in all_orders\n",
|
||||
" ]\n",
|
||||
" loads = [model.NewIntVar(0, max_capacity, \"load_of_slab_%i\" % s) for s in all_slabs]\n",
|
||||
" color_is_in_slab = [\n",
|
||||
" [model.NewBoolVar(\"color_%i_in_slab_%i\" % (c + 1, s)) for c in all_colors]\n",
|
||||
" for s in all_slabs\n",
|
||||
" ]\n",
|
||||
" color_is_in_slab = [[\n",
|
||||
" model.NewBoolVar('color_%i_in_slab_%i' % (c + 1, s)) for c in all_colors\n",
|
||||
" ] for s in all_slabs]\n",
|
||||
"\n",
|
||||
" # Compute load of all slabs.\n",
|
||||
" for s in all_slabs:\n",
|
||||
@@ -411,8 +284,7 @@
|
||||
" for s in all_slabs:\n",
|
||||
" for o in orders_per_color[c]:\n",
|
||||
" model.AddImplication(assign[o][s], color_is_in_slab[s][c])\n",
|
||||
" model.AddImplication(color_is_in_slab[s][c].Not(),\n",
|
||||
" assign[o][s].Not())\n",
|
||||
" model.AddImplication(color_is_in_slab[s][c].Not(), assign[o][s].Not())\n",
|
||||
"\n",
|
||||
" # At most two colors per slab.\n",
|
||||
" for s in all_slabs:\n",
|
||||
@@ -447,35 +319,39 @@
|
||||
" if w not in local_width_to_order:\n",
|
||||
" local_width_to_order[w] = []\n",
|
||||
" local_width_to_order[w].append(o)\n",
|
||||
" for w, os in local_width_to_order.items():\n",
|
||||
" for _, os in local_width_to_order.items():\n",
|
||||
" if len(os) > 1:\n",
|
||||
" for p in range(len(os) - 1):\n",
|
||||
" ordered_equivalent_orders.append((os[p], os[p + 1]))\n",
|
||||
" for w, os in width_to_unique_color_order.items():\n",
|
||||
" for _, os in width_to_unique_color_order.items():\n",
|
||||
" if len(os) > 1:\n",
|
||||
" for p in range(len(os) - 1):\n",
|
||||
" ordered_equivalent_orders.append((os[p], os[p + 1]))\n",
|
||||
"\n",
|
||||
" # Create position variables if there are symmetries to be broken.\n",
|
||||
" if break_symmetries and ordered_equivalent_orders:\n",
|
||||
" print(' - creating %i symmetry breaking constraints' %\n",
|
||||
" len(ordered_equivalent_orders))\n",
|
||||
" print(\n",
|
||||
" \" - creating %i symmetry breaking constraints\"\n",
|
||||
" % len(ordered_equivalent_orders)\n",
|
||||
" )\n",
|
||||
" positions = {}\n",
|
||||
" for p in ordered_equivalent_orders:\n",
|
||||
" if p[0] not in positions:\n",
|
||||
" positions[p[0]] = model.NewIntVar(0, num_slabs - 1,\n",
|
||||
" 'position_of_slab_%i' % p[0])\n",
|
||||
" positions[p[0]] = model.NewIntVar(\n",
|
||||
" 0, num_slabs - 1, \"position_of_slab_%i\" % p[0]\n",
|
||||
" )\n",
|
||||
" model.AddMapDomain(positions[p[0]], assign[p[0]])\n",
|
||||
" if p[1] not in positions:\n",
|
||||
" positions[p[1]] = model.NewIntVar(0, num_slabs - 1,\n",
|
||||
" 'position_of_slab_%i' % p[1])\n",
|
||||
" positions[p[1]] = model.NewIntVar(\n",
|
||||
" 0, num_slabs - 1, \"position_of_slab_%i\" % p[1]\n",
|
||||
" )\n",
|
||||
" model.AddMapDomain(positions[p[1]], assign[p[1]])\n",
|
||||
" # Finally add the symmetry breaking constraint.\n",
|
||||
" model.Add(positions[p[0]] <= positions[p[1]])\n",
|
||||
"\n",
|
||||
" # Objective.\n",
|
||||
" obj = model.NewIntVar(0, num_slabs * max_loss, 'obj')\n",
|
||||
" losses = [model.NewIntVar(0, max_loss, 'loss_%i' % s) for s in all_slabs]\n",
|
||||
" obj = model.NewIntVar(0, num_slabs * max_loss, \"obj\")\n",
|
||||
" losses = [model.NewIntVar(0, max_loss, \"loss_%i\" % s) for s in all_slabs]\n",
|
||||
" for s in all_slabs:\n",
|
||||
" model.AddElement(loads[s], loss_array, losses[s])\n",
|
||||
" model.Add(obj == sum(losses))\n",
|
||||
@@ -483,17 +359,19 @@
|
||||
"\n",
|
||||
" ### Solve model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" solver.parameters.num_search_workers = 8\n",
|
||||
" if _PARAMS.value:\n",
|
||||
" text_format.Parse(_PARAMS.value, solver.parameters)\n",
|
||||
" objective_printer = cp_model.ObjectiveSolutionPrinter()\n",
|
||||
" status = solver.Solve(model, objective_printer)\n",
|
||||
"\n",
|
||||
" ### Output the solution.\n",
|
||||
" if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):\n",
|
||||
" print(\n",
|
||||
" 'Loss = %i, time = %f s, %i conflicts' %\n",
|
||||
" (solver.ObjectiveValue(), solver.WallTime(), solver.NumConflicts()))\n",
|
||||
" \"Loss = %i, time = %f s, %i conflicts\"\n",
|
||||
" % (solver.ObjectiveValue(), solver.WallTime(), solver.NumConflicts())\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('No solution')\n",
|
||||
" print(\"No solution\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def collect_valid_slabs_dp(capacities, colors, widths, loss_array):\n",
|
||||
@@ -502,13 +380,11 @@
|
||||
"\n",
|
||||
" max_capacity = max(capacities)\n",
|
||||
"\n",
|
||||
" valid_assignment = collections.namedtuple('valid_assignment',\n",
|
||||
" 'orders load colors')\n",
|
||||
" valid_assignment = collections.namedtuple(\"valid_assignment\", \"orders load colors\")\n",
|
||||
" all_valid_assignments = [valid_assignment(orders=[], load=0, colors=[])]\n",
|
||||
"\n",
|
||||
" for order_id in range(len(colors)):\n",
|
||||
" for order_id, new_color in enumerate(colors):\n",
|
||||
" new_width = widths[order_id]\n",
|
||||
" new_color = colors[order_id]\n",
|
||||
" new_assignments = []\n",
|
||||
" for assignment in all_valid_assignments:\n",
|
||||
" if assignment.load + new_width > max_capacity:\n",
|
||||
@@ -518,18 +394,21 @@
|
||||
" new_colors.append(new_color)\n",
|
||||
" if len(new_colors) > 2:\n",
|
||||
" continue\n",
|
||||
" new_assignment = valid_assignment(orders=assignment.orders +\n",
|
||||
" [order_id],\n",
|
||||
" load=assignment.load + new_width,\n",
|
||||
" colors=new_colors)\n",
|
||||
" new_assignment = valid_assignment(\n",
|
||||
" orders=assignment.orders + [order_id],\n",
|
||||
" load=assignment.load + new_width,\n",
|
||||
" colors=new_colors,\n",
|
||||
" )\n",
|
||||
" new_assignments.append(new_assignment)\n",
|
||||
" all_valid_assignments.extend(new_assignments)\n",
|
||||
"\n",
|
||||
" print('%i assignments created in %.2f s' %\n",
|
||||
" (len(all_valid_assignments), time.time() - start_time))\n",
|
||||
" print(\n",
|
||||
" \"%i assignments created in %.2f s\"\n",
|
||||
" % (len(all_valid_assignments), time.time() - start_time)\n",
|
||||
" )\n",
|
||||
" tuples = []\n",
|
||||
" for assignment in all_valid_assignments:\n",
|
||||
" solution = [0 for _ in range(len(colors))]\n",
|
||||
" solution = [0] * len(colors)\n",
|
||||
" for i in assignment.orders:\n",
|
||||
" solution[i] = 1\n",
|
||||
" solution.append(loss_array[assignment.load])\n",
|
||||
@@ -549,16 +428,17 @@
|
||||
" all_slabs = range(num_slabs)\n",
|
||||
" all_colors = range(num_colors)\n",
|
||||
" all_orders = range(len(orders))\n",
|
||||
" print('Solving steel mill with %i orders, %i slabs, and %i capacities' %\n",
|
||||
" (num_orders, num_slabs, num_capacities - 1))\n",
|
||||
" print(\n",
|
||||
" \"Solving steel mill with %i orders, %i slabs, and %i capacities\"\n",
|
||||
" % (num_orders, num_slabs, num_capacities - 1)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Compute auxiliary data.\n",
|
||||
" widths = [x[0] for x in orders]\n",
|
||||
" colors = [x[1] for x in orders]\n",
|
||||
" max_capacity = max(capacities)\n",
|
||||
" loss_array = [\n",
|
||||
" min(x for x in capacities if x >= c) - c for c in range(max_capacity +\n",
|
||||
" 1)\n",
|
||||
" min(x for x in capacities if x >= c) - c for c in range(max_capacity + 1)\n",
|
||||
" ]\n",
|
||||
" max_loss = max(loss_array)\n",
|
||||
"\n",
|
||||
@@ -566,21 +446,23 @@
|
||||
"\n",
|
||||
" # Create the model and the decision variables.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
" assign = [[\n",
|
||||
" model.NewBoolVar('assign_%i_to_slab_%i' % (o, s)) for s in all_slabs\n",
|
||||
" ] for o in all_orders]\n",
|
||||
" loads = [model.NewIntVar(0, max_capacity, 'load_%i' % s) for s in all_slabs]\n",
|
||||
" losses = [model.NewIntVar(0, max_loss, 'loss_%i' % s) for s in all_slabs]\n",
|
||||
" assign = [\n",
|
||||
" [model.NewBoolVar(\"assign_%i_to_slab_%i\" % (o, s)) for s in all_slabs]\n",
|
||||
" for o in all_orders\n",
|
||||
" ]\n",
|
||||
" loads = [model.NewIntVar(0, max_capacity, \"load_%i\" % s) for s in all_slabs]\n",
|
||||
" losses = [model.NewIntVar(0, max_loss, \"loss_%i\" % s) for s in all_slabs]\n",
|
||||
"\n",
|
||||
" unsorted_valid_slabs = collect_valid_slabs_dp(capacities, colors, widths,\n",
|
||||
" loss_array)\n",
|
||||
" unsorted_valid_slabs = collect_valid_slabs_dp(\n",
|
||||
" capacities, colors, widths, loss_array\n",
|
||||
" )\n",
|
||||
" # Sort slab by descending load/loss. Remove duplicates.\n",
|
||||
" valid_slabs = sorted(unsorted_valid_slabs,\n",
|
||||
" key=lambda c: 1000 * c[-1] + c[-2])\n",
|
||||
" valid_slabs = sorted(unsorted_valid_slabs, key=lambda c: 1000 * c[-1] + c[-2])\n",
|
||||
"\n",
|
||||
" for s in all_slabs:\n",
|
||||
" model.AddAllowedAssignments([assign[o][s] for o in all_orders] +\n",
|
||||
" [losses[s], loads[s]], valid_slabs)\n",
|
||||
" model.AddAllowedAssignments(\n",
|
||||
" [assign[o][s] for o in all_orders] + [losses[s], loads[s]], valid_slabs\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Orders are assigned to one slab.\n",
|
||||
" for o in all_orders:\n",
|
||||
@@ -595,7 +477,7 @@
|
||||
"\n",
|
||||
" # Collect equivalent orders.\n",
|
||||
" if break_symmetries:\n",
|
||||
" print('Breaking symmetries')\n",
|
||||
" print(\"Breaking symmetries\")\n",
|
||||
" width_to_unique_color_order = {}\n",
|
||||
" ordered_equivalent_orders = []\n",
|
||||
" orders_per_color = [\n",
|
||||
@@ -619,28 +501,32 @@
|
||||
" if w not in local_width_to_order:\n",
|
||||
" local_width_to_order[w] = []\n",
|
||||
" local_width_to_order[w].append(o)\n",
|
||||
" for w, os in local_width_to_order.items():\n",
|
||||
" for _, os in local_width_to_order.items():\n",
|
||||
" if len(os) > 1:\n",
|
||||
" for p in range(len(os) - 1):\n",
|
||||
" ordered_equivalent_orders.append((os[p], os[p + 1]))\n",
|
||||
" for w, os in width_to_unique_color_order.items():\n",
|
||||
" for _, os in width_to_unique_color_order.items():\n",
|
||||
" if len(os) > 1:\n",
|
||||
" for p in range(len(os) - 1):\n",
|
||||
" ordered_equivalent_orders.append((os[p], os[p + 1]))\n",
|
||||
"\n",
|
||||
" # Create position variables if there are symmetries to be broken.\n",
|
||||
" if ordered_equivalent_orders:\n",
|
||||
" print(' - creating %i symmetry breaking constraints' %\n",
|
||||
" len(ordered_equivalent_orders))\n",
|
||||
" print(\n",
|
||||
" \" - creating %i symmetry breaking constraints\"\n",
|
||||
" % len(ordered_equivalent_orders)\n",
|
||||
" )\n",
|
||||
" positions = {}\n",
|
||||
" for p in ordered_equivalent_orders:\n",
|
||||
" if p[0] not in positions:\n",
|
||||
" positions[p[0]] = model.NewIntVar(\n",
|
||||
" 0, num_slabs - 1, 'position_of_slab_%i' % p[0])\n",
|
||||
" 0, num_slabs - 1, \"position_of_slab_%i\" % p[0]\n",
|
||||
" )\n",
|
||||
" model.AddMapDomain(positions[p[0]], assign[p[0]])\n",
|
||||
" if p[1] not in positions:\n",
|
||||
" positions[p[1]] = model.NewIntVar(\n",
|
||||
" 0, num_slabs - 1, 'position_of_slab_%i' % p[1])\n",
|
||||
" 0, num_slabs - 1, \"position_of_slab_%i\" % p[1]\n",
|
||||
" )\n",
|
||||
" model.AddMapDomain(positions[p[1]], assign[p[1]])\n",
|
||||
" # Finally add the symmetry breaking constraint.\n",
|
||||
" model.Add(positions[p[0]] <= positions[p[1]])\n",
|
||||
@@ -648,22 +534,24 @@
|
||||
" # Objective.\n",
|
||||
" model.Minimize(sum(losses))\n",
|
||||
"\n",
|
||||
" print('Model created')\n",
|
||||
" print(\"Model created\")\n",
|
||||
"\n",
|
||||
" ### Solve model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" solver.num_search_workers = 8\n",
|
||||
" solution_printer = SteelMillSlabSolutionPrinter(orders, assign, loads,\n",
|
||||
" losses)\n",
|
||||
" if _PARAMS.value:\n",
|
||||
" text_format.Parse(_PARAMS.value, solver.parameters)\n",
|
||||
"\n",
|
||||
" solution_printer = SteelMillSlabSolutionPrinter(orders, assign, loads, losses)\n",
|
||||
" status = solver.Solve(model, solution_printer)\n",
|
||||
"\n",
|
||||
" ### Output the solution.\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" print(\n",
|
||||
" 'Loss = %i, time = %.2f s, %i conflicts' %\n",
|
||||
" (solver.ObjectiveValue(), solver.WallTime(), solver.NumConflicts()))\n",
|
||||
" \"Loss = %i, time = %.2f s, %i conflicts\"\n",
|
||||
" % (solver.ObjectiveValue(), solver.WallTime(), solver.NumConflicts())\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('No solution')\n",
|
||||
" print(\"No solution\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def steel_mill_slab_with_column_generation(problem):\n",
|
||||
@@ -674,76 +562,76 @@
|
||||
" num_orders = len(orders)\n",
|
||||
" num_capacities = len(capacities)\n",
|
||||
" all_orders = range(len(orders))\n",
|
||||
" print('Solving steel mill with %i orders, %i slabs, and %i capacities' %\n",
|
||||
" (num_orders, num_slabs, num_capacities - 1))\n",
|
||||
" print(\n",
|
||||
" \"Solving steel mill with %i orders, %i slabs, and %i capacities\"\n",
|
||||
" % (num_orders, num_slabs, num_capacities - 1)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Compute auxiliary data.\n",
|
||||
" widths = [x[0] for x in orders]\n",
|
||||
" colors = [x[1] for x in orders]\n",
|
||||
" max_capacity = max(capacities)\n",
|
||||
" loss_array = [\n",
|
||||
" min(x for x in capacities if x >= c) - c for c in range(max_capacity +\n",
|
||||
" 1)\n",
|
||||
" min(x for x in capacities if x >= c) - c for c in range(max_capacity + 1)\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" ### Model problem.\n",
|
||||
"\n",
|
||||
" # Generate all valid slabs (columns)\n",
|
||||
" unsorted_valid_slabs = collect_valid_slabs_dp(capacities, colors, widths,\n",
|
||||
" loss_array)\n",
|
||||
" unsorted_valid_slabs = collect_valid_slabs_dp(\n",
|
||||
" capacities, colors, widths, loss_array\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Sort slab by descending load/loss. Remove duplicates.\n",
|
||||
" valid_slabs = sorted(unsorted_valid_slabs,\n",
|
||||
" key=lambda c: 1000 * c[-1] + c[-2])\n",
|
||||
" valid_slabs = sorted(unsorted_valid_slabs, key=lambda c: 1000 * c[-1] + c[-2])\n",
|
||||
" all_valid_slabs = range(len(valid_slabs))\n",
|
||||
"\n",
|
||||
" # create model and decision variables.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
" selected = [model.NewBoolVar('selected_%i' % i) for i in all_valid_slabs]\n",
|
||||
" selected = [model.NewBoolVar(\"selected_%i\" % i) for i in all_valid_slabs]\n",
|
||||
"\n",
|
||||
" for order_id in all_orders:\n",
|
||||
" model.Add(\n",
|
||||
" sum(selected[i]\n",
|
||||
" for i, slab in enumerate(valid_slabs)\n",
|
||||
" if slab[order_id]) == 1)\n",
|
||||
" sum(selected[i] for i, slab in enumerate(valid_slabs) if slab[order_id])\n",
|
||||
" == 1\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Redundant constraint (sum of loads == sum of widths).\n",
|
||||
" model.Add(\n",
|
||||
" sum(selected[i] * valid_slabs[i][-1]\n",
|
||||
" for i in all_valid_slabs) == sum(widths))\n",
|
||||
" sum(selected[i] * valid_slabs[i][-1] for i in all_valid_slabs) == sum(widths)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Objective.\n",
|
||||
" model.Minimize(\n",
|
||||
" sum(selected[i] * valid_slabs[i][-2] for i in all_valid_slabs))\n",
|
||||
" model.Minimize(sum(selected[i] * valid_slabs[i][-2] for i in all_valid_slabs))\n",
|
||||
"\n",
|
||||
" print('Model created')\n",
|
||||
" print(\"Model created\")\n",
|
||||
"\n",
|
||||
" ### Solve model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" solver.parameters.num_search_workers = 8\n",
|
||||
" solver.parameters.log_search_progress = True\n",
|
||||
" if _PARAMS.value:\n",
|
||||
" text_format.Parse(_PARAMS.value, solver.parameters)\n",
|
||||
" solution_printer = cp_model.ObjectiveSolutionPrinter()\n",
|
||||
" status = solver.Solve(model, solution_printer)\n",
|
||||
"\n",
|
||||
" ### Output the solution.\n",
|
||||
" if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):\n",
|
||||
" print(\n",
|
||||
" 'Loss = %i, time = %.2f s, %i conflicts' %\n",
|
||||
" (solver.ObjectiveValue(), solver.WallTime(), solver.NumConflicts()))\n",
|
||||
" \"Loss = %i, time = %.2f s, %i conflicts\"\n",
|
||||
" % (solver.ObjectiveValue(), solver.WallTime(), solver.NumConflicts())\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('No solution')\n",
|
||||
" print(\"No solution\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(_):\n",
|
||||
" if _SOLVER.value == 'sat':\n",
|
||||
" if _SOLVER.value == \"sat\":\n",
|
||||
" steel_mill_slab(_PROBLEM.value, _BREAK_SYMMETRIES.value)\n",
|
||||
" elif _SOLVER.value == 'sat_table':\n",
|
||||
" steel_mill_slab_with_valid_slabs(_PROBLEM.value,\n",
|
||||
" _BREAK_SYMMETRIES.value)\n",
|
||||
" elif _SOLVER.value == 'sat_column':\n",
|
||||
" elif _SOLVER.value == \"sat_table\":\n",
|
||||
" steel_mill_slab_with_valid_slabs(_PROBLEM.value, _BREAK_SYMMETRIES.value)\n",
|
||||
" elif _SOLVER.value == \"sat_column\":\n",
|
||||
" steel_mill_slab_with_column_generation(_PROBLEM.value)\n",
|
||||
" else:\n",
|
||||
" print(f'Unknown model {_SOLVER.value}')\n",
|
||||
" print(f\"Unknown model {_SOLVER.value}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"This model implements a sudoku solver.\n"
|
||||
]
|
||||
},
|
||||
@@ -95,16 +96,22 @@
|
||||
" line = list(range(0, line_size))\n",
|
||||
" cell = list(range(0, cell_size))\n",
|
||||
"\n",
|
||||
" initial_grid = [[0, 6, 0, 0, 5, 0, 0, 2, 0], [0, 0, 0, 3, 0, 0, 0, 9, 0],\n",
|
||||
" [7, 0, 0, 6, 0, 0, 0, 1, 0], [0, 0, 6, 0, 3, 0, 4, 0, 0],\n",
|
||||
" [0, 0, 4, 0, 7, 0, 1, 0, 0], [0, 0, 5, 0, 9, 0, 8, 0, 0],\n",
|
||||
" [0, 4, 0, 0, 0, 1, 0, 0, 6], [0, 3, 0, 0, 0, 8, 0, 0, 0],\n",
|
||||
" [0, 2, 0, 0, 4, 0, 0, 5, 0]]\n",
|
||||
" initial_grid = [\n",
|
||||
" [0, 6, 0, 0, 5, 0, 0, 2, 0],\n",
|
||||
" [0, 0, 0, 3, 0, 0, 0, 9, 0],\n",
|
||||
" [7, 0, 0, 6, 0, 0, 0, 1, 0],\n",
|
||||
" [0, 0, 6, 0, 3, 0, 4, 0, 0],\n",
|
||||
" [0, 0, 4, 0, 7, 0, 1, 0, 0],\n",
|
||||
" [0, 0, 5, 0, 9, 0, 8, 0, 0],\n",
|
||||
" [0, 4, 0, 0, 0, 1, 0, 0, 6],\n",
|
||||
" [0, 3, 0, 0, 0, 8, 0, 0, 0],\n",
|
||||
" [0, 2, 0, 0, 4, 0, 0, 5, 0],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" grid = {}\n",
|
||||
" for i in line:\n",
|
||||
" for j in line:\n",
|
||||
" grid[(i, j)] = model.NewIntVar(1, line_size, 'grid %i %i' % (i, j))\n",
|
||||
" grid[(i, j)] = model.NewIntVar(1, line_size, \"grid %i %i\" % (i, j))\n",
|
||||
"\n",
|
||||
" # AllDifferent on rows.\n",
|
||||
" for i in line:\n",
|
||||
@@ -120,8 +127,7 @@
|
||||
" one_cell = []\n",
|
||||
" for di in cell:\n",
|
||||
" for dj in cell:\n",
|
||||
" one_cell.append(grid[(i * cell_size + di,\n",
|
||||
" j * cell_size + dj)])\n",
|
||||
" one_cell.append(grid[(i * cell_size + di, j * cell_size + dj)])\n",
|
||||
"\n",
|
||||
" model.AddAllDifferent(one_cell)\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"CP-SAT model for task allocation problem.\n",
|
||||
"\n",
|
||||
"see http://yetanothermathprogrammingconsultant.blogspot.com/2018/09/minizinc-\n",
|
||||
@@ -93,256 +94,210 @@
|
||||
"def task_allocation_sat():\n",
|
||||
" \"\"\"Solves the task allocation problem.\"\"\"\n",
|
||||
" # Availability matrix.\n",
|
||||
" available = [[\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0\n",
|
||||
" available = [\n",
|
||||
" # fmt:off\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 1, 1, 1, 1, 1, 1, 1, 1\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,\n",
|
||||
" 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 1, 1, 1, 1, 1, 1, 1, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,\n",
|
||||
" 1, 1, 1, 1, 1, 1, 1, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,\n",
|
||||
" 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,\n",
|
||||
" 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 1, 1, 1, 1, 1, 1, 1, 1, 1\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,\n",
|
||||
" 1, 1, 1, 1, 1, 1, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 1, 1, 1, 1, 1, 1, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,\n",
|
||||
" 1, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,\n",
|
||||
" 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,\n",
|
||||
" 1, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,\n",
|
||||
" 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ]]\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,\n",
|
||||
" 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,\n",
|
||||
" 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,\n",
|
||||
" 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n",
|
||||
" ],\n",
|
||||
" # fmt:on\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" ntasks = len(available)\n",
|
||||
" nslots = len(available[0])\n",
|
||||
@@ -359,30 +314,31 @@
|
||||
" assign = {}\n",
|
||||
" for task in all_tasks:\n",
|
||||
" for slot in all_slots:\n",
|
||||
" assign[(task, slot)] = model.NewBoolVar('x[%i][%i]' % (task, slot))\n",
|
||||
" count = model.NewIntVar(0, nslots, 'count')\n",
|
||||
" slot_used = [model.NewBoolVar('slot_used[%i]' % s) for s in all_slots]\n",
|
||||
" assign[(task, slot)] = model.NewBoolVar(\"x[%i][%i]\" % (task, slot))\n",
|
||||
" count = model.NewIntVar(0, nslots, \"count\")\n",
|
||||
" slot_used = [model.NewBoolVar(\"slot_used[%i]\" % s) for s in all_slots]\n",
|
||||
"\n",
|
||||
" for task in all_tasks:\n",
|
||||
" model.Add(\n",
|
||||
" sum(assign[(task, slot)]\n",
|
||||
" for slot in all_slots\n",
|
||||
" if available[task][slot] == 1) == 1)\n",
|
||||
" sum(\n",
|
||||
" assign[(task, slot)] for slot in all_slots if available[task][slot] == 1\n",
|
||||
" )\n",
|
||||
" == 1\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" for slot in all_slots:\n",
|
||||
" model.Add(\n",
|
||||
" sum(assign[(task, slot)]\n",
|
||||
" for task in all_tasks\n",
|
||||
" if available[task][slot] == 1) <= capacity)\n",
|
||||
" model.AddBoolOr([\n",
|
||||
" assign[(task, slot)]\n",
|
||||
" for task in all_tasks\n",
|
||||
" if available[task][slot] == 1\n",
|
||||
" ]).OnlyEnforceIf(slot_used[slot])\n",
|
||||
" sum(\n",
|
||||
" assign[(task, slot)] for task in all_tasks if available[task][slot] == 1\n",
|
||||
" )\n",
|
||||
" <= capacity\n",
|
||||
" )\n",
|
||||
" model.AddBoolOr(\n",
|
||||
" [assign[(task, slot)] for task in all_tasks if available[task][slot] == 1]\n",
|
||||
" ).OnlyEnforceIf(slot_used[slot])\n",
|
||||
" for task in all_tasks:\n",
|
||||
" if available[task][slot] == 1:\n",
|
||||
" model.AddImplication(slot_used[slot].Not(),\n",
|
||||
" assign[(task, slot)].Not())\n",
|
||||
" model.AddImplication(slot_used[slot].Not(), assign[(task, slot)].Not())\n",
|
||||
" else:\n",
|
||||
" model.Add(assign[(task, slot)] == 0)\n",
|
||||
"\n",
|
||||
@@ -399,15 +355,15 @@
|
||||
" solver.parameters.num_search_workers = 16\n",
|
||||
" status = solver.Solve(model)\n",
|
||||
"\n",
|
||||
" print('Statistics')\n",
|
||||
" print(' - status =', solver.StatusName(status))\n",
|
||||
" print(' - optimal solution =', solver.ObjectiveValue())\n",
|
||||
" print(' - wall time : %f s' % solver.WallTime())\n",
|
||||
" print(\"Statistics\")\n",
|
||||
" print(\" - status =\", solver.StatusName(status))\n",
|
||||
" print(\" - optimal solution =\", solver.ObjectiveValue())\n",
|
||||
" print(\" - wall time : %f s\" % solver.WallTime())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" task_allocation_sat()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Tasks and workers to group assignment to average sum(cost) / #workers.\n"
|
||||
]
|
||||
},
|
||||
@@ -94,8 +95,10 @@
|
||||
" self.__solution_count = 0\n",
|
||||
"\n",
|
||||
" def on_solution_callback(self):\n",
|
||||
" print('Solution %i, time = %f s, objective = %i' %\n",
|
||||
" (self.__solution_count, self.WallTime(), self.ObjectiveValue()))\n",
|
||||
" print(\n",
|
||||
" \"Solution %i, time = %f s, objective = %i\"\n",
|
||||
" % (self.__solution_count, self.WallTime(), self.ObjectiveValue())\n",
|
||||
" )\n",
|
||||
" self.__solution_count += 1\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -118,13 +121,13 @@
|
||||
" x = {}\n",
|
||||
" for i in all_workers:\n",
|
||||
" for j in all_groups:\n",
|
||||
" x[i, j] = model.NewBoolVar('x[%i,%i]' % (i, j))\n",
|
||||
" x[i, j] = model.NewBoolVar(\"x[%i,%i]\" % (i, j))\n",
|
||||
"\n",
|
||||
" ## y_kj is 1 if task k is assigned to group j\n",
|
||||
" y = {}\n",
|
||||
" for k in all_tasks:\n",
|
||||
" for j in all_groups:\n",
|
||||
" y[k, j] = model.NewBoolVar('x[%i,%i]' % (k, j))\n",
|
||||
" y[k, j] = model.NewBoolVar(\"x[%i,%i]\" % (k, j))\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
"\n",
|
||||
@@ -143,13 +146,11 @@
|
||||
" scaled_sum_of_costs_in_group = []\n",
|
||||
" scaling = 1000 # We introduce scaling to deal with floating point average.\n",
|
||||
" for j in all_groups:\n",
|
||||
" n = model.NewIntVar(1, num_workers, 'num_workers_in_group_%i' % j)\n",
|
||||
" n = model.NewIntVar(1, num_workers, \"num_workers_in_group_%i\" % j)\n",
|
||||
" model.Add(n == sum(x[i, j] for i in all_workers))\n",
|
||||
" c = model.NewIntVar(0, sum_of_costs * scaling,\n",
|
||||
" 'sum_of_costs_of_group_%i' % j)\n",
|
||||
" c = model.NewIntVar(0, sum_of_costs * scaling, \"sum_of_costs_of_group_%i\" % j)\n",
|
||||
" model.Add(c == sum(y[k, j] * task_cost[k] * scaling for k in all_tasks))\n",
|
||||
" a = model.NewIntVar(0, sum_of_costs * scaling,\n",
|
||||
" 'average_cost_of_group_%i' % j)\n",
|
||||
" a = model.NewIntVar(0, sum_of_costs * scaling, \"average_cost_of_group_%i\" % j)\n",
|
||||
" model.AddDivisionEquality(a, c, n)\n",
|
||||
"\n",
|
||||
" averages.append(a)\n",
|
||||
@@ -160,7 +161,7 @@
|
||||
" model.Add(sum(num_workers_in_group) == num_workers)\n",
|
||||
"\n",
|
||||
" # Objective.\n",
|
||||
" obj = model.NewIntVar(0, sum_of_costs * scaling, 'obj')\n",
|
||||
" obj = model.NewIntVar(0, sum_of_costs * scaling, \"obj\")\n",
|
||||
" model.AddMaxEquality(obj, averages)\n",
|
||||
" model.Minimize(obj)\n",
|
||||
"\n",
|
||||
@@ -173,17 +174,18 @@
|
||||
"\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" for j in all_groups:\n",
|
||||
" print('Group %i' % j)\n",
|
||||
" print(\"Group %i\" % j)\n",
|
||||
" for i in all_workers:\n",
|
||||
" if solver.BooleanValue(x[i, j]):\n",
|
||||
" print(' - worker %i' % i)\n",
|
||||
" print(\" - worker %i\" % i)\n",
|
||||
" for k in all_tasks:\n",
|
||||
" if solver.BooleanValue(y[k, j]):\n",
|
||||
" print(' - task %i with cost %i' % (k, task_cost[k]))\n",
|
||||
" print(' - sum_of_costs = %i' %\n",
|
||||
" (solver.Value(scaled_sum_of_costs_in_group[j]) // scaling))\n",
|
||||
" print(' - average cost = %f' %\n",
|
||||
" (solver.Value(averages[j]) * 1.0 / scaling))\n",
|
||||
" print(\" - task %i with cost %i\" % (k, task_cost[k]))\n",
|
||||
" print(\n",
|
||||
" \" - sum_of_costs = %i\"\n",
|
||||
" % (solver.Value(scaled_sum_of_costs_in_group[j]) // scaling)\n",
|
||||
" )\n",
|
||||
" print(\" - average cost = %f\" % (solver.Value(averages[j]) * 1.0 / scaling))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"tasks_and_workers_assignment_sat()\n",
|
||||
@@ -191,7 +193,7 @@
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" tasks_and_workers_assignment_sat()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Simple travelling salesman problem between cities.\n"
|
||||
]
|
||||
},
|
||||
@@ -85,6 +86,7 @@
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"DISTANCE_MATRIX = [\n",
|
||||
" # fmt:off\n",
|
||||
" [0, 10938, 4542, 2835, 29441, 2171, 1611, 9208, 9528, 11111, 16120, 22606, 22127, 20627, 21246, 23387, 16697, 33609, 26184, 24772, 22644, 20655, 30492, 23296, 32979, 18141, 19248, 17129, 17192, 15645, 12658, 11210, 12094, 13175, 18162, 4968, 12308, 10084, 13026, 15056],\n",
|
||||
" [10938, 0, 6422, 9742, 18988, 12974, 11216, 19715, 19004, 18271, 25070, 31971, 31632, 30571, 31578, 33841, 27315, 43964, 36944, 35689, 33569, 31481, 41360, 33760, 43631, 28730, 29976, 27803, 28076, 26408, 23504, 22025, 22000, 13197, 14936, 15146, 23246, 20956, 23963, 25994],\n",
|
||||
" [4542, 6422, 0, 3644, 25173, 6552, 5092, 13584, 13372, 13766, 19805, 26537, 26117, 24804, 25590, 27784, 21148, 37981, 30693, 29315, 27148, 25071, 34943, 27472, 37281, 22389, 23592, 21433, 21655, 20011, 17087, 15612, 15872, 11653, 15666, 8842, 16843, 14618, 17563, 19589],\n",
|
||||
@@ -125,14 +127,15 @@
|
||||
" [10084, 20956, 14618, 12135, 38935, 8306, 9793, 2615, 5850, 10467, 9918, 14568, 13907, 11803, 11750, 13657, 6901, 23862, 16125, 14748, 12981, 11624, 21033, 15358, 24144, 10304, 10742, 9094, 8042, 7408, 4580, 4072, 8446, 20543, 26181, 7668, 2747, 0, 3330, 5313],\n",
|
||||
" [13026, 23963, 17563, 14771, 42160, 11069, 12925, 5730, 8778, 13375, 11235, 14366, 13621, 11188, 10424, 11907, 5609, 21861, 13624, 11781, 9718, 8304, 17737, 12200, 20816, 7330, 7532, 6117, 4735, 4488, 2599, 3355, 7773, 22186, 27895, 9742, 726, 3330, 0, 2042],\n",
|
||||
" [15056, 25994, 19589, 16743, 44198, 13078, 14967, 7552, 10422, 14935, 11891, 14002, 13225, 10671, 9475, 10633, 5084, 20315, 11866, 9802, 7682, 6471, 15720, 10674, 18908, 6204, 6000, 5066, 3039, 3721, 3496, 4772, 8614, 23805, 29519, 11614, 2749, 5313, 2042, 0],\n",
|
||||
"] # yapf: disable\n",
|
||||
" # fmt:on\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" \"\"\"Entry point of the program.\"\"\"\n",
|
||||
" num_nodes = len(DISTANCE_MATRIX)\n",
|
||||
" all_nodes = range(num_nodes)\n",
|
||||
" print('Num nodes =', num_nodes)\n",
|
||||
" print(\"Num nodes =\", num_nodes)\n",
|
||||
"\n",
|
||||
" # Model.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
@@ -148,8 +151,8 @@
|
||||
" if i == j:\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" lit = model.NewBoolVar('%i follows %i' % (j, i))\n",
|
||||
" arcs.append([i, j, lit])\n",
|
||||
" lit = model.NewBoolVar(\"%i follows %i\" % (j, i))\n",
|
||||
" arcs.append((i, j, lit))\n",
|
||||
" arc_literals[i, j] = lit\n",
|
||||
"\n",
|
||||
" obj_vars.append(lit)\n",
|
||||
@@ -158,8 +161,7 @@
|
||||
" model.AddCircuit(arcs)\n",
|
||||
"\n",
|
||||
" # Minimize weighted sum of arcs. Because this s\n",
|
||||
" model.Minimize(\n",
|
||||
" sum(obj_vars[i] * obj_coeffs[i] for i in range(len(obj_vars))))\n",
|
||||
" model.Minimize(sum(obj_vars[i] * obj_coeffs[i] for i in range(len(obj_vars))))\n",
|
||||
"\n",
|
||||
" # Solve and print out the solution.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
@@ -171,7 +173,7 @@
|
||||
" print(solver.ResponseStats())\n",
|
||||
"\n",
|
||||
" current_node = 0\n",
|
||||
" str_route = '%i' % current_node\n",
|
||||
" str_route = \"%i\" % current_node\n",
|
||||
" route_is_finished = False\n",
|
||||
" route_distance = 0\n",
|
||||
" while not route_is_finished:\n",
|
||||
@@ -179,15 +181,15 @@
|
||||
" if i == current_node:\n",
|
||||
" continue\n",
|
||||
" if solver.BooleanValue(arc_literals[current_node, i]):\n",
|
||||
" str_route += ' -> %i' % i\n",
|
||||
" str_route += \" -> %i\" % i\n",
|
||||
" route_distance += DISTANCE_MATRIX[current_node][i]\n",
|
||||
" current_node = i\n",
|
||||
" if current_node == 0:\n",
|
||||
" route_is_finished = True\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
" print('Route:', str_route)\n",
|
||||
" print('Travelled distance:', route_distance)\n",
|
||||
" print(\"Route:\", str_route)\n",
|
||||
" print(\"Travelled distance:\", route_distance)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solves a simple shift scheduling problem.\n"
|
||||
]
|
||||
},
|
||||
@@ -89,8 +90,15 @@
|
||||
"class SolutionPrinter(cp_model.CpSolverSolutionCallback):\n",
|
||||
" \"\"\"Print intermediate solutions.\"\"\"\n",
|
||||
"\n",
|
||||
" def __init__(self, num_vendors, num_hours, possible_schedules,\n",
|
||||
" selected_schedules, hours_stat, min_vendors):\n",
|
||||
" def __init__(\n",
|
||||
" self,\n",
|
||||
" num_vendors,\n",
|
||||
" num_hours,\n",
|
||||
" possible_schedules,\n",
|
||||
" selected_schedules,\n",
|
||||
" hours_stat,\n",
|
||||
" min_vendors,\n",
|
||||
" ):\n",
|
||||
" cp_model.CpSolverSolutionCallback.__init__(self)\n",
|
||||
" self.__solution_count = 0\n",
|
||||
" self.__num_vendors = num_vendors\n",
|
||||
@@ -103,17 +111,18 @@
|
||||
" def on_solution_callback(self):\n",
|
||||
" \"\"\"Called at each new solution.\"\"\"\n",
|
||||
" self.__solution_count += 1\n",
|
||||
" print('Solution %i: ', self.__solution_count)\n",
|
||||
" print(' min vendors:', self.__min_vendors)\n",
|
||||
" print(\"Solution %i: \", self.__solution_count)\n",
|
||||
" print(\" min vendors:\", self.__min_vendors)\n",
|
||||
" for i in range(self.__num_vendors):\n",
|
||||
" print(\n",
|
||||
" ' - vendor %i: ' % i, self.__possible_schedules[self.Value(\n",
|
||||
" self.__selected_schedules[i])])\n",
|
||||
" \" - vendor %i: \" % i,\n",
|
||||
" self.__possible_schedules[self.Value(self.__selected_schedules[i])],\n",
|
||||
" )\n",
|
||||
" print()\n",
|
||||
"\n",
|
||||
" for j in range(self.__num_hours):\n",
|
||||
" print(' - # workers on day%2i: ' % j, end=' ')\n",
|
||||
" print(self.Value(self.__hours_stat[j]), end=' ')\n",
|
||||
" print(\" - # workers on day%2i: \" % j, end=\" \")\n",
|
||||
" print(self.Value(self.__hours_stat[j]), end=\" \")\n",
|
||||
" print()\n",
|
||||
" print()\n",
|
||||
"\n",
|
||||
@@ -140,12 +149,14 @@
|
||||
" # Last columns are :\n",
|
||||
" # index_of_the_schedule, sum of worked hours (per work type).\n",
|
||||
" # The index is useful for branching.\n",
|
||||
" possible_schedules = [[1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 8],\n",
|
||||
" [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 4],\n",
|
||||
" [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 2, 5],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3, 4],\n",
|
||||
" [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 4, 3],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0]]\n",
|
||||
" possible_schedules = [\n",
|
||||
" [1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 8],\n",
|
||||
" [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 4],\n",
|
||||
" [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 2, 5],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3, 4],\n",
|
||||
" [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 4, 3],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" num_possible_schedules = len(possible_schedules)\n",
|
||||
" selected_schedules = []\n",
|
||||
@@ -165,11 +176,10 @@
|
||||
" for v in all_vendors:\n",
|
||||
" tmp = []\n",
|
||||
" for h in all_hours:\n",
|
||||
" x[v, h] = model.NewIntVar(0, num_work_types, 'x[%i,%i]' % (v, h))\n",
|
||||
" x[v, h] = model.NewIntVar(0, num_work_types, \"x[%i,%i]\" % (v, h))\n",
|
||||
" tmp.append(x[v, h])\n",
|
||||
" selected_schedule = model.NewIntVar(0, num_possible_schedules - 1,\n",
|
||||
" 's[%i]' % v)\n",
|
||||
" hours = model.NewIntVar(0, num_hours, 'h[%i]' % v)\n",
|
||||
" selected_schedule = model.NewIntVar(0, num_possible_schedules - 1, \"s[%i]\" % v)\n",
|
||||
" hours = model.NewIntVar(0, num_hours, \"h[%i]\" % v)\n",
|
||||
" selected_schedules.append(selected_schedule)\n",
|
||||
" vendors_stat.append(hours)\n",
|
||||
" tmp.append(selected_schedule)\n",
|
||||
@@ -181,7 +191,7 @@
|
||||
" # Statistics and constraints for each hour\n",
|
||||
" #\n",
|
||||
" for h in all_hours:\n",
|
||||
" workers = model.NewIntVar(0, 1000, 'workers[%i]' % h)\n",
|
||||
" workers = model.NewIntVar(0, 1000, \"workers[%i]\" % h)\n",
|
||||
" model.Add(workers == sum(x[v, h] for v in all_vendors))\n",
|
||||
" hours_stat.append(workers)\n",
|
||||
" model.Add(workers * max_traffic_per_vendor >= traffic[h])\n",
|
||||
@@ -195,23 +205,27 @@
|
||||
" # Solve model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" solver.parameters.enumerate_all_solutions = True\n",
|
||||
" solution_printer = SolutionPrinter(num_vendors, num_hours,\n",
|
||||
" possible_schedules, selected_schedules,\n",
|
||||
" hours_stat, min_vendors)\n",
|
||||
" solution_printer = SolutionPrinter(\n",
|
||||
" num_vendors,\n",
|
||||
" num_hours,\n",
|
||||
" possible_schedules,\n",
|
||||
" selected_schedules,\n",
|
||||
" hours_stat,\n",
|
||||
" min_vendors,\n",
|
||||
" )\n",
|
||||
" status = solver.Solve(model, solution_printer)\n",
|
||||
" print('Status = %s' % solver.StatusName(status))\n",
|
||||
" print(\"Status = %s\" % solver.StatusName(status))\n",
|
||||
"\n",
|
||||
" print('Statistics')\n",
|
||||
" print(' - conflicts : %i' % solver.NumConflicts())\n",
|
||||
" print(' - branches : %i' % solver.NumBranches())\n",
|
||||
" print(' - wall time : %f s' % solver.WallTime())\n",
|
||||
" print(' - number of solutions found: %i' %\n",
|
||||
" solution_printer.solution_count())\n",
|
||||
" print(\"Statistics\")\n",
|
||||
" print(\" - conflicts : %i\" % solver.NumConflicts())\n",
|
||||
" print(\" - branches : %i\" % solver.NumBranches())\n",
|
||||
" print(\" - wall time : %f s\" % solver.WallTime())\n",
|
||||
" print(\" - number of solutions found: %i\" % solution_printer.solution_count())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
" vendor_scheduling_sat()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Finding an optimal wedding seating chart.\n",
|
||||
"\n",
|
||||
"From\n",
|
||||
@@ -124,9 +125,10 @@
|
||||
" def on_solution_callback(self):\n",
|
||||
" current_time = time.time()\n",
|
||||
" objective = self.ObjectiveValue()\n",
|
||||
" print(\"Solution %i, time = %f s, objective = %i\" %\n",
|
||||
" (self.__solution_count, current_time - self.__start_time,\n",
|
||||
" objective))\n",
|
||||
" print(\n",
|
||||
" \"Solution %i, time = %f s, objective = %i\"\n",
|
||||
" % (self.__solution_count, current_time - self.__start_time, objective)\n",
|
||||
" )\n",
|
||||
" self.__solution_count += 1\n",
|
||||
"\n",
|
||||
" for t in range(self.__num_tables):\n",
|
||||
@@ -153,38 +155,52 @@
|
||||
"\n",
|
||||
" # Connection matrix: who knows who, and how strong\n",
|
||||
" # is the relation\n",
|
||||
" connections = [[1, 50, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [50, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 1, 50, 1, 1, 1, 1, 10, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 50, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 1, 1, 1, 50, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 1, 1, 50, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 1, 1, 1, 1, 1, 50, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 1, 1, 1, 1, 50, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 50, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]]\n",
|
||||
" connections = [\n",
|
||||
" [1, 50, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [50, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 1, 50, 1, 1, 1, 1, 10, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 50, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 1, 1, 1, 50, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 1, 1, 50, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 1, 1, 1, 1, 1, 50, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 1, 1, 1, 1, 50, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [1, 1, 10, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 50, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" # Names of the guests. B: Bride side, G: Groom side\n",
|
||||
" names = [\n",
|
||||
" \"Deb (B)\", \"John (B)\", \"Martha (B)\", \"Travis (B)\", \"Allan (B)\",\n",
|
||||
" \"Lois (B)\", \"Jayne (B)\", \"Brad (B)\", \"Abby (B)\", \"Mary Helen (G)\",\n",
|
||||
" \"Lee (G)\", \"Annika (G)\", \"Carl (G)\", \"Colin (G)\", \"Shirley (G)\",\n",
|
||||
" \"DeAnn (G)\", \"Lori (G)\"\n",
|
||||
" \"Deb (B)\",\n",
|
||||
" \"John (B)\",\n",
|
||||
" \"Martha (B)\",\n",
|
||||
" \"Travis (B)\",\n",
|
||||
" \"Allan (B)\",\n",
|
||||
" \"Lois (B)\",\n",
|
||||
" \"Jayne (B)\",\n",
|
||||
" \"Brad (B)\",\n",
|
||||
" \"Abby (B)\",\n",
|
||||
" \"Mary Helen (G)\",\n",
|
||||
" \"Lee (G)\",\n",
|
||||
" \"Annika (G)\",\n",
|
||||
" \"Carl (G)\",\n",
|
||||
" \"Colin (G)\",\n",
|
||||
" \"Shirley (G)\",\n",
|
||||
" \"DeAnn (G)\",\n",
|
||||
" \"Lori (G)\",\n",
|
||||
" ]\n",
|
||||
" return num_tables, table_capacity, min_known_neighbors, connections, names\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def solve_with_discrete_model():\n",
|
||||
" \"\"\"Discrete approach.\"\"\"\n",
|
||||
" num_tables, table_capacity, min_known_neighbors, connections, names = build_data(\n",
|
||||
" )\n",
|
||||
" num_tables, table_capacity, min_known_neighbors, connections, names = build_data()\n",
|
||||
"\n",
|
||||
" num_guests = len(connections)\n",
|
||||
"\n",
|
||||
@@ -200,28 +216,32 @@
|
||||
" seats = {}\n",
|
||||
" for t in all_tables:\n",
|
||||
" for g in all_guests:\n",
|
||||
" seats[(t,\n",
|
||||
" g)] = model.NewBoolVar(\"guest %i seats on table %i\" % (g, t))\n",
|
||||
" seats[(t, g)] = model.NewBoolVar(\"guest %i seats on table %i\" % (g, t))\n",
|
||||
"\n",
|
||||
" colocated = {}\n",
|
||||
" for g1 in range(num_guests - 1):\n",
|
||||
" for g2 in range(g1 + 1, num_guests):\n",
|
||||
" colocated[(g1, g2)] = model.NewBoolVar(\n",
|
||||
" \"guest %i seats with guest %i\" % (g1, g2))\n",
|
||||
" \"guest %i seats with guest %i\" % (g1, g2)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" same_table = {}\n",
|
||||
" for g1 in range(num_guests - 1):\n",
|
||||
" for g2 in range(g1 + 1, num_guests):\n",
|
||||
" for t in all_tables:\n",
|
||||
" same_table[(g1, g2, t)] = model.NewBoolVar(\n",
|
||||
" \"guest %i seats with guest %i on table %i\" % (g1, g2, t))\n",
|
||||
" \"guest %i seats with guest %i on table %i\" % (g1, g2, t)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Objective\n",
|
||||
" model.Maximize(\n",
|
||||
" sum(connections[g1][g2] * colocated[g1, g2]\n",
|
||||
" sum(\n",
|
||||
" connections[g1][g2] * colocated[g1, g2]\n",
|
||||
" for g1 in range(num_guests - 1)\n",
|
||||
" for g2 in range(g1 + 1, num_guests)\n",
|
||||
" if connections[g1][g2] > 0))\n",
|
||||
" if connections[g1][g2] > 0\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" #\n",
|
||||
" # Constraints\n",
|
||||
@@ -240,29 +260,38 @@
|
||||
" for g2 in range(g1 + 1, num_guests):\n",
|
||||
" for t in all_tables:\n",
|
||||
" # Link same_table and seats.\n",
|
||||
" model.AddBoolOr([\n",
|
||||
" seats[(t, g1)].Not(), seats[(t, g2)].Not(),\n",
|
||||
" same_table[(g1, g2, t)]\n",
|
||||
" ])\n",
|
||||
" model.AddBoolOr(\n",
|
||||
" [\n",
|
||||
" seats[(t, g1)].Not(),\n",
|
||||
" seats[(t, g2)].Not(),\n",
|
||||
" same_table[(g1, g2, t)],\n",
|
||||
" ]\n",
|
||||
" )\n",
|
||||
" model.AddImplication(same_table[(g1, g2, t)], seats[(t, g1)])\n",
|
||||
" model.AddImplication(same_table[(g1, g2, t)], seats[(t, g2)])\n",
|
||||
"\n",
|
||||
" # Link colocated and same_table.\n",
|
||||
" model.Add(\n",
|
||||
" sum(same_table[(g1, g2, t)] for t in all_tables) == colocated[(\n",
|
||||
" g1, g2)])\n",
|
||||
" sum(same_table[(g1, g2, t)] for t in all_tables) == colocated[(g1, g2)]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Min known neighbors rule.\n",
|
||||
" for g in all_guests:\n",
|
||||
" model.Add(\n",
|
||||
" sum(same_table[(g, g2, t)]\n",
|
||||
" sum(\n",
|
||||
" same_table[(g, g2, t)]\n",
|
||||
" for g2 in range(g + 1, num_guests)\n",
|
||||
" for t in all_tables\n",
|
||||
" if connections[g][g2] > 0) +\n",
|
||||
" sum(same_table[(g1, g, t)]\n",
|
||||
" if connections[g][g2] > 0\n",
|
||||
" )\n",
|
||||
" + sum(\n",
|
||||
" same_table[(g1, g, t)]\n",
|
||||
" for g1 in range(g)\n",
|
||||
" for t in all_tables\n",
|
||||
" if connections[g1][g] > 0) >= min_known_neighbors)\n",
|
||||
" if connections[g1][g] > 0\n",
|
||||
" )\n",
|
||||
" >= min_known_neighbors\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Symmetry breaking. First guest seats on the first table.\n",
|
||||
" model.Add(seats[(0, 0)] == 1)\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solve a random Weighted Latency problem with the CP-SAT solver.\n"
|
||||
]
|
||||
},
|
||||
@@ -87,16 +88,16 @@
|
||||
"from google.protobuf import text_format\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"_NUM_NODES = flags.DEFINE_integer('num_nodes', 12, 'Number of nodes to visit.')\n",
|
||||
"_GRID_SIZE = flags.DEFINE_integer('grid_size', 20,\n",
|
||||
" 'Size of the grid where nodes are.')\n",
|
||||
"_PROFIT_RANGE = flags.DEFINE_integer('profit_range', 50, 'Range of profit.')\n",
|
||||
"_SEED = flags.DEFINE_integer('seed', 0, 'Random seed.')\n",
|
||||
"_PARAMS = flags.DEFINE_string('params',\n",
|
||||
" 'num_search_workers:16, max_time_in_seconds:5',\n",
|
||||
" 'Sat solver parameters.')\n",
|
||||
"_NUM_NODES = flags.DEFINE_integer(\"num_nodes\", 12, \"Number of nodes to visit.\")\n",
|
||||
"_GRID_SIZE = flags.DEFINE_integer(\"grid_size\", 20, \"Size of the grid where nodes are.\")\n",
|
||||
"_PROFIT_RANGE = flags.DEFINE_integer(\"profit_range\", 50, \"Range of profit.\")\n",
|
||||
"_SEED = flags.DEFINE_integer(\"seed\", 0, \"Random seed.\")\n",
|
||||
"_PARAMS = flags.DEFINE_string(\n",
|
||||
" \"params\", \"num_search_workers:16, max_time_in_seconds:5\", \"Sat solver parameters.\"\n",
|
||||
")\n",
|
||||
"_PROTO_FILE = flags.DEFINE_string(\n",
|
||||
" 'proto_file', '', 'If not empty, output the proto to this file.')\n",
|
||||
" \"proto_file\", \"\", \"If not empty, output the proto to this file.\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def build_model():\n",
|
||||
@@ -126,10 +127,7 @@
|
||||
"\n",
|
||||
" # because of the manhattan distance, the sum of distances is bounded by this.\n",
|
||||
" horizon = _GRID_SIZE.value * 2 * _NUM_NODES.value\n",
|
||||
" times = [\n",
|
||||
" model.NewIntVar(0, horizon, f'x_{i}')\n",
|
||||
" for i in range(_NUM_NODES.value + 1)\n",
|
||||
" ]\n",
|
||||
" times = [model.NewIntVar(0, horizon, f\"x_{i}\") for i in range(_NUM_NODES.value + 1)]\n",
|
||||
"\n",
|
||||
" # Node 0 is the start node.\n",
|
||||
" model.Add(times[0] == 0)\n",
|
||||
@@ -142,7 +140,7 @@
|
||||
" continue\n",
|
||||
" # We use a manhattan distance between nodes.\n",
|
||||
" distance = abs(x[i] - x[j]) + abs(y[i] - y[j])\n",
|
||||
" lit = model.NewBoolVar(f'{i}_to_{j}')\n",
|
||||
" lit = model.NewBoolVar(f\"{i}_to_{j}\")\n",
|
||||
" arcs.append((i, j, lit))\n",
|
||||
"\n",
|
||||
" # Add transitions between nodes.\n",
|
||||
@@ -169,7 +167,7 @@
|
||||
"\n",
|
||||
"def main(argv: Sequence[str]) -> None:\n",
|
||||
" if len(argv) > 1:\n",
|
||||
" raise app.UsageError('Too many command-line arguments.')\n",
|
||||
" raise app.UsageError(\"Too many command-line arguments.\")\n",
|
||||
"\n",
|
||||
" x, y, profits = build_model()\n",
|
||||
" solve_with_cp_sat(x, y, profits)\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"This is the zebra problem as invented by Lewis Caroll.\n",
|
||||
"\n",
|
||||
"There are five houses.\n",
|
||||
@@ -112,42 +113,41 @@
|
||||
" # Create the model.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
" red = model.NewIntVar(1, 5, 'red')\n",
|
||||
" green = model.NewIntVar(1, 5, 'green')\n",
|
||||
" yellow = model.NewIntVar(1, 5, 'yellow')\n",
|
||||
" blue = model.NewIntVar(1, 5, 'blue')\n",
|
||||
" ivory = model.NewIntVar(1, 5, 'ivory')\n",
|
||||
" red = model.NewIntVar(1, 5, \"red\")\n",
|
||||
" green = model.NewIntVar(1, 5, \"green\")\n",
|
||||
" yellow = model.NewIntVar(1, 5, \"yellow\")\n",
|
||||
" blue = model.NewIntVar(1, 5, \"blue\")\n",
|
||||
" ivory = model.NewIntVar(1, 5, \"ivory\")\n",
|
||||
"\n",
|
||||
" englishman = model.NewIntVar(1, 5, 'englishman')\n",
|
||||
" spaniard = model.NewIntVar(1, 5, 'spaniard')\n",
|
||||
" japanese = model.NewIntVar(1, 5, 'japanese')\n",
|
||||
" ukrainian = model.NewIntVar(1, 5, 'ukrainian')\n",
|
||||
" norwegian = model.NewIntVar(1, 5, 'norwegian')\n",
|
||||
" englishman = model.NewIntVar(1, 5, \"englishman\")\n",
|
||||
" spaniard = model.NewIntVar(1, 5, \"spaniard\")\n",
|
||||
" japanese = model.NewIntVar(1, 5, \"japanese\")\n",
|
||||
" ukrainian = model.NewIntVar(1, 5, \"ukrainian\")\n",
|
||||
" norwegian = model.NewIntVar(1, 5, \"norwegian\")\n",
|
||||
"\n",
|
||||
" dog = model.NewIntVar(1, 5, 'dog')\n",
|
||||
" snails = model.NewIntVar(1, 5, 'snails')\n",
|
||||
" fox = model.NewIntVar(1, 5, 'fox')\n",
|
||||
" zebra = model.NewIntVar(1, 5, 'zebra')\n",
|
||||
" horse = model.NewIntVar(1, 5, 'horse')\n",
|
||||
" dog = model.NewIntVar(1, 5, \"dog\")\n",
|
||||
" snails = model.NewIntVar(1, 5, \"snails\")\n",
|
||||
" fox = model.NewIntVar(1, 5, \"fox\")\n",
|
||||
" zebra = model.NewIntVar(1, 5, \"zebra\")\n",
|
||||
" horse = model.NewIntVar(1, 5, \"horse\")\n",
|
||||
"\n",
|
||||
" tea = model.NewIntVar(1, 5, 'tea')\n",
|
||||
" coffee = model.NewIntVar(1, 5, 'coffee')\n",
|
||||
" water = model.NewIntVar(1, 5, 'water')\n",
|
||||
" milk = model.NewIntVar(1, 5, 'milk')\n",
|
||||
" fruit_juice = model.NewIntVar(1, 5, 'fruit juice')\n",
|
||||
" tea = model.NewIntVar(1, 5, \"tea\")\n",
|
||||
" coffee = model.NewIntVar(1, 5, \"coffee\")\n",
|
||||
" water = model.NewIntVar(1, 5, \"water\")\n",
|
||||
" milk = model.NewIntVar(1, 5, \"milk\")\n",
|
||||
" fruit_juice = model.NewIntVar(1, 5, \"fruit juice\")\n",
|
||||
"\n",
|
||||
" old_gold = model.NewIntVar(1, 5, 'old gold')\n",
|
||||
" kools = model.NewIntVar(1, 5, 'kools')\n",
|
||||
" chesterfields = model.NewIntVar(1, 5, 'chesterfields')\n",
|
||||
" lucky_strike = model.NewIntVar(1, 5, 'lucky strike')\n",
|
||||
" parliaments = model.NewIntVar(1, 5, 'parliaments')\n",
|
||||
" old_gold = model.NewIntVar(1, 5, \"old gold\")\n",
|
||||
" kools = model.NewIntVar(1, 5, \"kools\")\n",
|
||||
" chesterfields = model.NewIntVar(1, 5, \"chesterfields\")\n",
|
||||
" lucky_strike = model.NewIntVar(1, 5, \"lucky strike\")\n",
|
||||
" parliaments = model.NewIntVar(1, 5, \"parliaments\")\n",
|
||||
"\n",
|
||||
" model.AddAllDifferent(red, green, yellow, blue, ivory)\n",
|
||||
" model.AddAllDifferent(englishman, spaniard, japanese, ukrainian, norwegian)\n",
|
||||
" model.AddAllDifferent(dog, snails, fox, zebra, horse)\n",
|
||||
" model.AddAllDifferent(tea, coffee, water, milk, fruit_juice)\n",
|
||||
" model.AddAllDifferent(parliaments, kools, chesterfields, lucky_strike,\n",
|
||||
" old_gold)\n",
|
||||
" model.AddAllDifferent(parliaments, kools, chesterfields, lucky_strike, old_gold)\n",
|
||||
"\n",
|
||||
" model.Add(englishman == red)\n",
|
||||
" model.Add(spaniard == dog)\n",
|
||||
@@ -159,18 +159,18 @@
|
||||
" model.Add(milk == 3)\n",
|
||||
" model.Add(norwegian == 1)\n",
|
||||
"\n",
|
||||
" diff_fox_chesterfields = model.NewIntVar(-4, 4, 'diff_fox_chesterfields')\n",
|
||||
" diff_fox_chesterfields = model.NewIntVar(-4, 4, \"diff_fox_chesterfields\")\n",
|
||||
" model.Add(diff_fox_chesterfields == fox - chesterfields)\n",
|
||||
" model.AddAbsEquality(1, diff_fox_chesterfields)\n",
|
||||
"\n",
|
||||
" diff_horse_kools = model.NewIntVar(-4, 4, 'diff_horse_kools')\n",
|
||||
" diff_horse_kools = model.NewIntVar(-4, 4, \"diff_horse_kools\")\n",
|
||||
" model.Add(diff_horse_kools == horse - kools)\n",
|
||||
" model.AddAbsEquality(1, diff_horse_kools)\n",
|
||||
"\n",
|
||||
" model.Add(lucky_strike == fruit_juice)\n",
|
||||
" model.Add(japanese == parliaments)\n",
|
||||
"\n",
|
||||
" diff_norwegian_blue = model.NewIntVar(-4, 4, 'diff_norwegian_blue')\n",
|
||||
" diff_norwegian_blue = model.NewIntVar(-4, 4, \"diff_norwegian_blue\")\n",
|
||||
" model.Add(diff_norwegian_blue == norwegian - blue)\n",
|
||||
" model.AddAbsEquality(1, diff_norwegian_blue)\n",
|
||||
"\n",
|
||||
@@ -180,16 +180,12 @@
|
||||
"\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" people = [englishman, spaniard, japanese, ukrainian, norwegian]\n",
|
||||
" water_drinker = [\n",
|
||||
" p for p in people if solver.Value(p) == solver.Value(water)\n",
|
||||
" ][0]\n",
|
||||
" zebra_owner = [\n",
|
||||
" p for p in people if solver.Value(p) == solver.Value(zebra)\n",
|
||||
" ][0]\n",
|
||||
" print('The', water_drinker.Name(), 'drinks water.')\n",
|
||||
" print('The', zebra_owner.Name(), 'owns the zebra.')\n",
|
||||
" water_drinker = [p for p in people if solver.Value(p) == solver.Value(water)][0]\n",
|
||||
" zebra_owner = [p for p in people if solver.Value(p) == solver.Value(zebra)][0]\n",
|
||||
" print(\"The\", water_drinker.Name(), \"drinks water.\")\n",
|
||||
" print(\"The\", zebra_owner.Name(), \"owns the zebra.\")\n",
|
||||
" else:\n",
|
||||
" print('No solutions to the zebra problem, this is unusual!')\n",
|
||||
" print(\"No solutions to the zebra problem, this is unusual!\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"solve_zebra()\n",
|
||||
|
||||
@@ -92,17 +92,20 @@
|
||||
" \"\"\"Linear Sum Assignment example.\"\"\"\n",
|
||||
" assignment = linear_sum_assignment.SimpleLinearSumAssignment()\n",
|
||||
"\n",
|
||||
" costs = np.array([\n",
|
||||
" [90, 76, 75, 70],\n",
|
||||
" [35, 85, 55, 65],\n",
|
||||
" [125, 95, 90, 105],\n",
|
||||
" [45, 110, 95, 115],\n",
|
||||
" ])\n",
|
||||
" costs = np.array(\n",
|
||||
" [\n",
|
||||
" [90, 76, 75, 70],\n",
|
||||
" [35, 85, 55, 65],\n",
|
||||
" [125, 95, 90, 105],\n",
|
||||
" [45, 110, 95, 115],\n",
|
||||
" ]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Let's transform this into 3 parallel vectors (start_nodes, end_nodes,\n",
|
||||
" # arc_costs)\n",
|
||||
" end_nodes_unraveled, start_nodes_unraveled = np.meshgrid(\n",
|
||||
" np.arange(costs.shape[1]), np.arange(costs.shape[0]))\n",
|
||||
" np.arange(costs.shape[1]), np.arange(costs.shape[0])\n",
|
||||
" )\n",
|
||||
" start_nodes = start_nodes_unraveled.ravel()\n",
|
||||
" end_nodes = end_nodes_unraveled.ravel()\n",
|
||||
" arc_costs = costs.ravel()\n",
|
||||
@@ -112,15 +115,16 @@
|
||||
" status = assignment.solve()\n",
|
||||
"\n",
|
||||
" if status == assignment.OPTIMAL:\n",
|
||||
" print(f'Total cost = {assignment.optimal_cost()}\\n')\n",
|
||||
" print(f\"Total cost = {assignment.optimal_cost()}\\n\")\n",
|
||||
" for i in range(0, assignment.num_nodes()):\n",
|
||||
" print(f'Worker {i} assigned to task {assignment.right_mate(i)}.' +\n",
|
||||
" f' Cost = {assignment.assignment_cost(i)}')\n",
|
||||
" print(\n",
|
||||
" f\"Worker {i} assigned to task {assignment.right_mate(i)}.\"\n",
|
||||
" + f\" Cost = {assignment.assignment_cost(i)}\"\n",
|
||||
" )\n",
|
||||
" elif status == assignment.INFEASIBLE:\n",
|
||||
" print('No assignment is possible.')\n",
|
||||
" print(\"No assignment is possible.\")\n",
|
||||
" elif status == assignment.POSSIBLE_OVERFLOW:\n",
|
||||
" print(\n",
|
||||
" 'Some input costs are too large and may cause an integer overflow.')\n",
|
||||
" print(\"Some input costs are too large and may cause an integer overflow.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -92,18 +92,20 @@
|
||||
" smcf = min_cost_flow.SimpleMinCostFlow()\n",
|
||||
"\n",
|
||||
" # Define the directed graph for the flow.\n",
|
||||
" start_nodes = [0, 0, 0, 0] + [\n",
|
||||
" 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4\n",
|
||||
" ] + [5, 6, 7, 8]\n",
|
||||
" end_nodes = [1, 2, 3, 4] + [5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8\n",
|
||||
" ] + [9, 9, 9, 9]\n",
|
||||
" capacities = [1, 1, 1, 1] + [\n",
|
||||
" 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n",
|
||||
" ] + [1, 1, 1, 1]\n",
|
||||
" start_nodes = (\n",
|
||||
" [0, 0, 0, 0] + [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4] + [5, 6, 7, 8]\n",
|
||||
" )\n",
|
||||
" end_nodes = (\n",
|
||||
" [1, 2, 3, 4] + [5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8, 5, 6, 7, 8] + [9, 9, 9, 9]\n",
|
||||
" )\n",
|
||||
" capacities = (\n",
|
||||
" [1, 1, 1, 1] + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + [1, 1, 1, 1]\n",
|
||||
" )\n",
|
||||
" costs = (\n",
|
||||
" [0, 0, 0, 0] +\n",
|
||||
" [90, 76, 75, 70, 35, 85, 55, 65, 125, 95, 90, 105, 45, 110, 95, 115] +\n",
|
||||
" [0, 0, 0, 0])\n",
|
||||
" [0, 0, 0, 0]\n",
|
||||
" + [90, 76, 75, 70, 35, 85, 55, 65, 125, 95, 90, 105, 45, 110, 95, 115]\n",
|
||||
" + [0, 0, 0, 0]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" source = 0\n",
|
||||
" sink = 9\n",
|
||||
@@ -112,8 +114,9 @@
|
||||
"\n",
|
||||
" # Add each arc.\n",
|
||||
" for i in range(len(start_nodes)):\n",
|
||||
" smcf.add_arc_with_capacity_and_unit_cost(start_nodes[i], end_nodes[i],\n",
|
||||
" capacities[i], costs[i])\n",
|
||||
" smcf.add_arc_with_capacity_and_unit_cost(\n",
|
||||
" start_nodes[i], end_nodes[i], capacities[i], costs[i]\n",
|
||||
" )\n",
|
||||
" # Add node supplies.\n",
|
||||
" for i in range(len(supplies)):\n",
|
||||
" smcf.set_node_supply(i, supplies[i])\n",
|
||||
@@ -122,20 +125,21 @@
|
||||
" status = smcf.solve()\n",
|
||||
"\n",
|
||||
" if status == smcf.OPTIMAL:\n",
|
||||
" print('Total cost = ', smcf.optimal_cost())\n",
|
||||
" print(\"Total cost = \", smcf.optimal_cost())\n",
|
||||
" print()\n",
|
||||
" 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",
|
||||
" print('Worker %d assigned to task %d. Cost = %d' %\n",
|
||||
" (smcf.tail(arc), smcf.head(arc), smcf.unit_cost(arc)))\n",
|
||||
" print(\n",
|
||||
" \"Worker %d assigned to task %d. Cost = %d\"\n",
|
||||
" % (smcf.tail(arc), smcf.head(arc), smcf.unit_cost(arc))\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('There was an issue with the min cost flow input.')\n",
|
||||
" print(f'Status: {status}')\n",
|
||||
" print(\"There was an issue with the min cost flow input.\")\n",
|
||||
" print(f\"Status: {status}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -94,20 +94,42 @@
|
||||
" team_a = [1, 3, 5]\n",
|
||||
" team_b = [2, 4, 6]\n",
|
||||
"\n",
|
||||
" start_nodes = ([0, 0] + [11, 11, 11] + [12, 12, 12] + [\n",
|
||||
" 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6\n",
|
||||
" ] + [7, 8, 9, 10])\n",
|
||||
" end_nodes = ([11, 12] + team_a + team_b + [\n",
|
||||
" 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8,\n",
|
||||
" 9, 10\n",
|
||||
" ] + [13, 13, 13, 13])\n",
|
||||
" capacities = ([2, 2] + [1, 1, 1] + [1, 1, 1] + [\n",
|
||||
" 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1\n",
|
||||
" ] + [1, 1, 1, 1])\n",
|
||||
" costs = ([0, 0] + [0, 0, 0] + [0, 0, 0] + [\n",
|
||||
" 90, 76, 75, 70, 35, 85, 55, 65, 125, 95, 90, 105, 45, 110, 95, 115, 60,\n",
|
||||
" 105, 80, 75, 45, 65, 110, 95\n",
|
||||
" ] + [0, 0, 0, 0])\n",
|
||||
" start_nodes = (\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 0]\n",
|
||||
" + [11, 11, 11]\n",
|
||||
" + [12, 12, 12]\n",
|
||||
" + [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6]\n",
|
||||
" + [7, 8, 9, 10]\n",
|
||||
" # fmt: on\n",
|
||||
" )\n",
|
||||
" end_nodes = (\n",
|
||||
" # fmt: off\n",
|
||||
" [11, 12]\n",
|
||||
" + team_a\n",
|
||||
" + team_b\n",
|
||||
" + [7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10]\n",
|
||||
" + [13, 13, 13, 13]\n",
|
||||
" # fmt: on\n",
|
||||
" )\n",
|
||||
" capacities = (\n",
|
||||
" # fmt: off\n",
|
||||
" [2, 2]\n",
|
||||
" + [1, 1, 1]\n",
|
||||
" + [1, 1, 1]\n",
|
||||
" + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n",
|
||||
" + [1, 1, 1, 1]\n",
|
||||
" # fmt: on\n",
|
||||
" )\n",
|
||||
" costs = (\n",
|
||||
" # fmt: off\n",
|
||||
" [0, 0]\n",
|
||||
" + [0, 0, 0]\n",
|
||||
" + [0, 0, 0]\n",
|
||||
" + [90, 76, 75, 70, 35, 85, 55, 65, 125, 95, 90, 105, 45, 110, 95, 115, 60, 105, 80, 75, 45, 65, 110, 95]\n",
|
||||
" + [0, 0, 0, 0]\n",
|
||||
" # fmt: on\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" source = 0\n",
|
||||
" sink = 13\n",
|
||||
@@ -117,8 +139,9 @@
|
||||
"\n",
|
||||
" # Add each arc.\n",
|
||||
" for i in range(0, len(start_nodes)):\n",
|
||||
" smcf.add_arc_with_capacity_and_unit_cost(start_nodes[i], end_nodes[i],\n",
|
||||
" capacities[i], costs[i])\n",
|
||||
" smcf.add_arc_with_capacity_and_unit_cost(\n",
|
||||
" start_nodes[i], end_nodes[i], capacities[i], costs[i]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Add node supplies.\n",
|
||||
" for i in range(0, len(supplies)):\n",
|
||||
@@ -128,21 +151,26 @@
|
||||
" status = smcf.solve()\n",
|
||||
"\n",
|
||||
" if status == smcf.OPTIMAL:\n",
|
||||
" print('Total cost = ', smcf.optimal_cost())\n",
|
||||
" print(\"Total cost = \", smcf.optimal_cost())\n",
|
||||
" print()\n",
|
||||
" for arc in range(smcf.num_arcs()):\n",
|
||||
" # Can ignore arcs leading out of source or intermediate, or into sink.\n",
|
||||
" if (smcf.tail(arc) != source and smcf.tail(arc) != 11 and\n",
|
||||
" smcf.tail(arc) != 12 and smcf.head(arc) != sink):\n",
|
||||
"\n",
|
||||
" if (\n",
|
||||
" smcf.tail(arc) != source\n",
|
||||
" and smcf.tail(arc) != 11\n",
|
||||
" and smcf.tail(arc) != 12\n",
|
||||
" and smcf.head(arc) != sink\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",
|
||||
" print('Worker %d assigned to task %d. Cost = %d' %\n",
|
||||
" (smcf.tail(arc), smcf.head(arc), smcf.unit_cost(arc)))\n",
|
||||
" print(\n",
|
||||
" \"Worker %d assigned to task %d. Cost = %d\"\n",
|
||||
" % (smcf.tail(arc), smcf.head(arc), smcf.unit_cost(arc))\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('There was an issue with the min cost flow input.')\n",
|
||||
" print(f'Status: {status}')\n",
|
||||
" print(\"There was an issue with the min cost flow input.\")\n",
|
||||
" print(f\"Status: {status}\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -108,17 +108,17 @@
|
||||
" status = smf.solve(0, 4)\n",
|
||||
"\n",
|
||||
" if status != smf.OPTIMAL:\n",
|
||||
" print('There was an issue with the max flow input.')\n",
|
||||
" print(f'Status: {status}')\n",
|
||||
" print(\"There was an issue with the max flow input.\")\n",
|
||||
" print(f\"Status: {status}\")\n",
|
||||
" exit(1)\n",
|
||||
" print('Max flow:', smf.optimal_flow())\n",
|
||||
" print('')\n",
|
||||
" print(' Arc Flow / Capacity')\n",
|
||||
" print(\"Max flow:\", smf.optimal_flow())\n",
|
||||
" print(\"\")\n",
|
||||
" print(\" Arc Flow / Capacity\")\n",
|
||||
" solution_flows = smf.flows(all_arcs)\n",
|
||||
" for arc, flow, capacity in zip(all_arcs, solution_flows, capacities):\n",
|
||||
" print(f'{smf.tail(arc)} / {smf.head(arc)} {flow:3} / {capacity:3}')\n",
|
||||
" print('Source side min-cut:', smf.get_source_side_min_cut())\n",
|
||||
" print('Sink side min-cut:', smf.get_sink_side_min_cut())\n",
|
||||
" print(f\"{smf.tail(arc)} / {smf.head(arc)} {flow:3} / {capacity:3}\")\n",
|
||||
" print(\"Source side min-cut:\", smf.get_source_side_min_cut())\n",
|
||||
" print(\"Sink side min-cut:\", smf.get_sink_side_min_cut())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -106,7 +106,8 @@
|
||||
"\n",
|
||||
" # Add arcs, capacities and costs in bulk using numpy.\n",
|
||||
" all_arcs = smcf.add_arcs_with_capacity_and_unit_cost(\n",
|
||||
" start_nodes, end_nodes, capacities, unit_costs)\n",
|
||||
" start_nodes, end_nodes, capacities, unit_costs\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Add supply for each nodes.\n",
|
||||
" smcf.set_nodes_supplies(np.arange(0, len(supplies)), supplies)\n",
|
||||
@@ -115,17 +116,17 @@
|
||||
" status = smcf.solve()\n",
|
||||
"\n",
|
||||
" if status != smcf.OPTIMAL:\n",
|
||||
" print('There was an issue with the min cost flow input.')\n",
|
||||
" print(f'Status: {status}')\n",
|
||||
" print(\"There was an issue with the min cost flow input.\")\n",
|
||||
" print(f\"Status: {status}\")\n",
|
||||
" exit(1)\n",
|
||||
" print(f'Minimum cost: {smcf.optimal_cost()}')\n",
|
||||
" print('')\n",
|
||||
" print(' Arc Flow / Capacity Cost')\n",
|
||||
" print(f\"Minimum cost: {smcf.optimal_cost()}\")\n",
|
||||
" print(\"\")\n",
|
||||
" print(\" Arc Flow / Capacity Cost\")\n",
|
||||
" solution_flows = smcf.flows(all_arcs)\n",
|
||||
" costs = solution_flows * unit_costs\n",
|
||||
" for arc, flow, cost in zip(all_arcs, solution_flows, costs):\n",
|
||||
" print(\n",
|
||||
" f'{smcf.tail(arc):1} -> {smcf.head(arc)} {flow:3} / {smcf.capacity(arc):3} {cost}'\n",
|
||||
" f\"{smcf.tail(arc):1} -> {smcf.head(arc)} {flow:3} / {smcf.capacity(arc):3} {cost}\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
"\n",
|
||||
" # Solver.\n",
|
||||
" # Create the mip solver with the SCIP backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('SCIP')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"SCIP\")\n",
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
@@ -142,27 +142,26 @@
|
||||
" x = {}\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" x[worker, task] = solver.BoolVar(f'x[{worker},{task}]')\n",
|
||||
" x[worker, task] = solver.BoolVar(f\"x[{worker},{task}]\")\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
" # The total size of the tasks each worker takes on is at most total_size_max.\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" solver.Add(\n",
|
||||
" solver.Sum([x[worker, task] for task in range(num_tasks)]) <= 1)\n",
|
||||
" solver.Add(solver.Sum([x[worker, task] for task in range(num_tasks)]) <= 1)\n",
|
||||
"\n",
|
||||
" # Each task is assigned to exactly one worker.\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" solver.Add(\n",
|
||||
" solver.Sum([x[worker, task] for worker in range(num_workers)]) == 1)\n",
|
||||
" solver.Add(solver.Sum([x[worker, task] for worker in range(num_workers)]) == 1)\n",
|
||||
"\n",
|
||||
" # Create variables for each worker, indicating whether they work on some task.\n",
|
||||
" work = {}\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" work[worker] = solver.BoolVar(f'work[{worker}]')\n",
|
||||
" work[worker] = solver.BoolVar(f\"work[{worker}]\")\n",
|
||||
"\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" solver.Add(work[worker] == solver.Sum(\n",
|
||||
" [x[worker, task] for task in range(num_tasks)]))\n",
|
||||
" solver.Add(\n",
|
||||
" work[worker] == solver.Sum([x[worker, task] for task in range(num_tasks)])\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Group1\n",
|
||||
" constraint_g1 = solver.Constraint(1, 1)\n",
|
||||
@@ -172,7 +171,7 @@
|
||||
" constraint = solver.Constraint(0, 1)\n",
|
||||
" constraint.SetCoefficient(work[group1[i][0]], 1)\n",
|
||||
" constraint.SetCoefficient(work[group1[i][1]], 1)\n",
|
||||
" p = solver.BoolVar(f'g1_p{i}')\n",
|
||||
" p = solver.BoolVar(f\"g1_p{i}\")\n",
|
||||
" constraint.SetCoefficient(p, -2)\n",
|
||||
"\n",
|
||||
" constraint_g1.SetCoefficient(p, 1)\n",
|
||||
@@ -185,7 +184,7 @@
|
||||
" constraint = solver.Constraint(0, 1)\n",
|
||||
" constraint.SetCoefficient(work[group2[i][0]], 1)\n",
|
||||
" constraint.SetCoefficient(work[group2[i][1]], 1)\n",
|
||||
" p = solver.BoolVar(f'g2_p{i}')\n",
|
||||
" p = solver.BoolVar(f\"g2_p{i}\")\n",
|
||||
" constraint.SetCoefficient(p, -2)\n",
|
||||
"\n",
|
||||
" constraint_g2.SetCoefficient(p, 1)\n",
|
||||
@@ -198,7 +197,7 @@
|
||||
" constraint = solver.Constraint(0, 1)\n",
|
||||
" constraint.SetCoefficient(work[group3[i][0]], 1)\n",
|
||||
" constraint.SetCoefficient(work[group3[i][1]], 1)\n",
|
||||
" p = solver.BoolVar(f'g3_p{i}')\n",
|
||||
" p = solver.BoolVar(f\"g3_p{i}\")\n",
|
||||
" constraint.SetCoefficient(p, -2)\n",
|
||||
"\n",
|
||||
" constraint_g3.SetCoefficient(p, 1)\n",
|
||||
@@ -215,14 +214,16 @@
|
||||
"\n",
|
||||
" # Print solution.\n",
|
||||
" if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE:\n",
|
||||
" print(f'Total cost = {solver.Objective().Value()}\\n')\n",
|
||||
" print(f\"Total cost = {solver.Objective().Value()}\\n\")\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" if x[worker, task].solution_value() > 0.5:\n",
|
||||
" print(f'Worker {worker} assigned to task {task}.' +\n",
|
||||
" f' Cost: {costs[worker][task]}')\n",
|
||||
" print(\n",
|
||||
" f\"Worker {worker} assigned to task {task}.\"\n",
|
||||
" + f\" Cost: {costs[worker][task]}\"\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('No solution found.')\n",
|
||||
" print(\"No solution found.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"MIP example that solves an assignment problem."
|
||||
]
|
||||
},
|
||||
@@ -82,59 +83,76 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"import io\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"from ortools.linear_solver.python import model_builder\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" # Data\n",
|
||||
" costs = np.array([\n",
|
||||
" [90, 80, 75, 70],\n",
|
||||
" [35, 85, 55, 65],\n",
|
||||
" [125, 95, 90, 95],\n",
|
||||
" [45, 110, 95, 115],\n",
|
||||
" [50, 100, 90, 100],\n",
|
||||
" ])\n",
|
||||
" num_workers, num_tasks = costs.shape\n",
|
||||
" data_str = \"\"\"\n",
|
||||
" worker task cost\n",
|
||||
" w1 t1 90\n",
|
||||
" w1 t2 80\n",
|
||||
" w1 t3 75\n",
|
||||
" w1 t4 70\n",
|
||||
" w2 t1 35\n",
|
||||
" w2 t2 85\n",
|
||||
" w2 t3 55\n",
|
||||
" w2 t4 65\n",
|
||||
" w3 t1 125\n",
|
||||
" w3 t2 95\n",
|
||||
" w3 t3 90\n",
|
||||
" w3 t4 95\n",
|
||||
" w4 t1 45\n",
|
||||
" w4 t2 110\n",
|
||||
" w4 t3 95\n",
|
||||
" w4 t4 115\n",
|
||||
" w5 t1 50\n",
|
||||
" w5 t2 110\n",
|
||||
" w5 t3 90\n",
|
||||
" w5 t4 100\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" data = pd.read_table(io.StringIO(data_str), sep=r\"\\s+\")\n",
|
||||
"\n",
|
||||
" # Solver\n",
|
||||
" # Create the model.\n",
|
||||
" model = model_builder.ModelBuilder()\n",
|
||||
"\n",
|
||||
" # Variables\n",
|
||||
" # x[i, j] is an array of 0-1 variables, which will be 1\n",
|
||||
" # if worker i is assigned to task j.\n",
|
||||
" x = model.new_bool_var_array(shape=[num_workers, num_tasks], name='x') # pytype: disable=wrong-arg-types # numpy-scalars\n",
|
||||
" x = model.new_bool_var_series(name=\"x\", index=data.index)\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
" # Each worker is assigned to at most 1 task.\n",
|
||||
" for i in range(num_workers):\n",
|
||||
" model.add(np.sum(x[i, :]) <= 1)\n",
|
||||
" for unused_name, tasks in data.groupby(\"worker\"):\n",
|
||||
" model.add(x[tasks.index].sum() <= 1)\n",
|
||||
"\n",
|
||||
" # Each task is assigned to exactly one worker.\n",
|
||||
" for j in range(num_tasks):\n",
|
||||
" model.add(np.sum(x[:, j]) == 1)\n",
|
||||
" for unused_name, workers in data.groupby(\"task\"):\n",
|
||||
" model.add(x[workers.index].sum() == 1)\n",
|
||||
"\n",
|
||||
" # Objective\n",
|
||||
" model.minimize(np.dot(x.flatten(), costs.flatten()))\n",
|
||||
" model.minimize(data.cost.dot(x))\n",
|
||||
"\n",
|
||||
" # Create the solver with the CP-SAT backend, and solve the model.\n",
|
||||
" solver = model_builder.ModelSolver('sat')\n",
|
||||
" solver = model_builder.ModelSolver(\"sat\")\n",
|
||||
" status = solver.solve(model)\n",
|
||||
"\n",
|
||||
" # Print solution.\n",
|
||||
" if (status == model_builder.SolveStatus.OPTIMAL or\n",
|
||||
" status == model_builder.SolveStatus.FEASIBLE):\n",
|
||||
" print(f'Total cost = {solver.objective_value}\\n')\n",
|
||||
" for i in range(num_workers):\n",
|
||||
" for j in range(num_tasks):\n",
|
||||
" # Test if x[i,j] is 1 (with tolerance for floating point arithmetic).\n",
|
||||
" if solver.value(x[i, j]) > 0.5:\n",
|
||||
" print(f'Worker {i} assigned to task {j}.' +\n",
|
||||
" f' Cost: {costs[i][j]}')\n",
|
||||
" if (\n",
|
||||
" status == model_builder.SolveStatus.OPTIMAL\n",
|
||||
" or status == model_builder.SolveStatus.FEASIBLE\n",
|
||||
" ):\n",
|
||||
" print(f\"Total cost = {solver.objective_value}\\n\")\n",
|
||||
" selected = data.loc[solver.values(x).loc[lambda x: x == 1].index]\n",
|
||||
" for unused_index, row in selected.iterrows():\n",
|
||||
" print(f\"{row.task} assigned to {row.worker} with a cost of {row.cost}\")\n",
|
||||
" else:\n",
|
||||
" print('No solution found.')\n",
|
||||
" print(\"No solution found.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"MIP example that solves an assignment problem."
|
||||
]
|
||||
},
|
||||
@@ -99,7 +100,7 @@
|
||||
"\n",
|
||||
" # Solver\n",
|
||||
" # Create the mip solver with the SCIP backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('SCIP')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"SCIP\")\n",
|
||||
"\n",
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
@@ -110,7 +111,7 @@
|
||||
" x = {}\n",
|
||||
" for i in range(num_workers):\n",
|
||||
" for j in range(num_tasks):\n",
|
||||
" x[i, j] = solver.IntVar(0, 1, '')\n",
|
||||
" x[i, j] = solver.IntVar(0, 1, \"\")\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
" # Each worker is assigned to at most 1 task.\n",
|
||||
@@ -133,15 +134,14 @@
|
||||
"\n",
|
||||
" # Print solution.\n",
|
||||
" if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE:\n",
|
||||
" print(f'Total cost = {solver.Objective().Value()}\\n')\n",
|
||||
" print(f\"Total cost = {solver.Objective().Value()}\\n\")\n",
|
||||
" for i in range(num_workers):\n",
|
||||
" for j in range(num_tasks):\n",
|
||||
" # Test if x[i,j] is 1 (with tolerance for floating point arithmetic).\n",
|
||||
" if x[i, j].solution_value() > 0.5:\n",
|
||||
" print(f'Worker {i} assigned to task {j}.' +\n",
|
||||
" f' Cost: {costs[i][j]}')\n",
|
||||
" print(f\"Worker {i} assigned to task {j}.\" + f\" Cost: {costs[i][j]}\")\n",
|
||||
" else:\n",
|
||||
" print('No solution found.')\n",
|
||||
" print(\"No solution found.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
"\n",
|
||||
" # Solver\n",
|
||||
" # Create the mip solver with the SCIP backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('SCIP')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"SCIP\")\n",
|
||||
"\n",
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
@@ -120,20 +120,21 @@
|
||||
" x = {}\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" x[worker, task] = solver.BoolVar(f'x[{worker},{task}]')\n",
|
||||
" x[worker, task] = solver.BoolVar(f\"x[{worker},{task}]\")\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
" # The total size of the tasks each worker takes on is at most total_size_max.\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" solver.Add(\n",
|
||||
" solver.Sum([\n",
|
||||
" task_sizes[task] * x[worker, task] for task in range(num_tasks)\n",
|
||||
" ]) <= total_size_max)\n",
|
||||
" solver.Sum(\n",
|
||||
" [task_sizes[task] * x[worker, task] for task in range(num_tasks)]\n",
|
||||
" )\n",
|
||||
" <= total_size_max\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Each task is assigned to exactly one worker.\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" solver.Add(\n",
|
||||
" solver.Sum([x[worker, task] for worker in range(num_workers)]) == 1)\n",
|
||||
" solver.Add(solver.Sum([x[worker, task] for worker in range(num_workers)]) == 1)\n",
|
||||
"\n",
|
||||
" # Objective\n",
|
||||
" objective_terms = []\n",
|
||||
@@ -147,14 +148,16 @@
|
||||
"\n",
|
||||
" # Print solution.\n",
|
||||
" if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE:\n",
|
||||
" print(f'Total cost = {solver.Objective().Value()}\\n')\n",
|
||||
" print(f\"Total cost = {solver.Objective().Value()}\\n\")\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" if x[worker, task].solution_value() > 0.5:\n",
|
||||
" print(f'Worker {worker} assigned to task {task}.' +\n",
|
||||
" f' Cost: {costs[worker][task]}')\n",
|
||||
" print(\n",
|
||||
" f\"Worker {worker} assigned to task {task}.\"\n",
|
||||
" + f\" Cost: {costs[worker][task]}\"\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('No solution found.')\n",
|
||||
" print(\"No solution found.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
"\n",
|
||||
" # Solver\n",
|
||||
" # Create the mip solver with the SCIP backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('SCIP')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"SCIP\")\n",
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
@@ -116,18 +116,16 @@
|
||||
" x = {}\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" x[worker, task] = solver.BoolVar(f'x[{worker},{task}]')\n",
|
||||
" x[worker, task] = solver.BoolVar(f\"x[{worker},{task}]\")\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
" # Each worker is assigned at most 1 task.\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" solver.Add(\n",
|
||||
" solver.Sum([x[worker, task] for task in range(num_tasks)]) <= 1)\n",
|
||||
" solver.Add(solver.Sum([x[worker, task] for task in range(num_tasks)]) <= 1)\n",
|
||||
"\n",
|
||||
" # Each task is assigned to exactly one worker.\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" solver.Add(\n",
|
||||
" solver.Sum([x[worker, task] for worker in range(num_workers)]) == 1)\n",
|
||||
" solver.Add(solver.Sum([x[worker, task] for worker in range(num_workers)]) == 1)\n",
|
||||
"\n",
|
||||
" # Each team takes at most two tasks.\n",
|
||||
" team1_tasks = []\n",
|
||||
@@ -154,15 +152,17 @@
|
||||
"\n",
|
||||
" # Print solution.\n",
|
||||
" if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE:\n",
|
||||
" print(f'Total cost = {solver.Objective().Value()}\\n')\n",
|
||||
" print(f\"Total cost = {solver.Objective().Value()}\\n\")\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" if x[worker, task].solution_value() > 0.5:\n",
|
||||
" print(f'Worker {worker} assigned to task {task}.' +\n",
|
||||
" f' Cost = {costs[worker][task]}')\n",
|
||||
" print(\n",
|
||||
" f\"Worker {worker} assigned to task {task}.\"\n",
|
||||
" + f\" Cost = {costs[worker][task]}\"\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('No solution found.')\n",
|
||||
" print(f'Time = {solver.WallTime()} ms')\n",
|
||||
" print(\"No solution found.\")\n",
|
||||
" print(f\"Time = {solver.WallTime()} ms\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Minimal example to call the GLOP solver."
|
||||
]
|
||||
},
|
||||
@@ -83,27 +84,26 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from ortools.linear_solver import pywraplp\n",
|
||||
"from ortools.init import pywrapinit\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" # Create the linear solver with the GLOP backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('GLOP')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"GLOP\")\n",
|
||||
" if not solver:\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 = solver.NumVar(0, 1, \"x\")\n",
|
||||
" y = solver.NumVar(0, 2, \"y\")\n",
|
||||
"\n",
|
||||
" print('Number of variables =', solver.NumVariables())\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 = solver.Constraint(0, 2, \"ct\")\n",
|
||||
" ct.SetCoefficient(x, 1)\n",
|
||||
" ct.SetCoefficient(y, 1)\n",
|
||||
"\n",
|
||||
" print('Number of constraints =', solver.NumConstraints())\n",
|
||||
" print(\"Number of constraints =\", solver.NumConstraints())\n",
|
||||
"\n",
|
||||
" # Create the objective function, 3 * x + y.\n",
|
||||
" objective = solver.Objective()\n",
|
||||
@@ -113,18 +113,12 @@
|
||||
"\n",
|
||||
" solver.Solve()\n",
|
||||
"\n",
|
||||
" print('Solution:')\n",
|
||||
" print('Objective value =', objective.Value())\n",
|
||||
" print('x =', x.solution_value())\n",
|
||||
" print('y =', y.solution_value())\n",
|
||||
" print(\"Solution:\")\n",
|
||||
" print(\"Objective value =\", objective.Value())\n",
|
||||
" print(\"x =\", x.solution_value())\n",
|
||||
" print(\"y =\", y.solution_value())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"pywrapinit.CppBridge.InitLogging('basic_example.py')\n",
|
||||
"cpp_flags = pywrapinit.CppFlags()\n",
|
||||
"cpp_flags.logtostderr = True\n",
|
||||
"cpp_flags.log_prefix = False\n",
|
||||
"pywrapinit.CppBridge.SetFlags(cpp_flags)\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
"\n"
|
||||
]
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solve a simple bin packing problem using a MIP solver."
|
||||
]
|
||||
},
|
||||
@@ -82,76 +83,103 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"import io\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"from ortools.linear_solver.python import model_builder\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Create the data for the example.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" weights = [48, 30, 19, 36, 36, 27, 42, 42, 36, 24, 30]\n",
|
||||
" data['weights'] = weights\n",
|
||||
" data['items'] = list(range(len(weights)))\n",
|
||||
" data['bins'] = data['items']\n",
|
||||
" data['bin_capacity'] = 100\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
" items_str = \"\"\"\n",
|
||||
" item weight\n",
|
||||
" i1 48\n",
|
||||
" i2 30\n",
|
||||
" i3 19\n",
|
||||
" i4 36\n",
|
||||
" i5 36\n",
|
||||
" i6 27\n",
|
||||
" i7 42\n",
|
||||
" i8 42\n",
|
||||
" i9 36\n",
|
||||
" i10 24\n",
|
||||
" i11 30\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" bins_str = \"\"\"\n",
|
||||
" bin capacity\n",
|
||||
" b1 100\n",
|
||||
" b2 100\n",
|
||||
" b3 100\n",
|
||||
" b4 100\n",
|
||||
" b5 100\n",
|
||||
" b6 100\n",
|
||||
" b7 100\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" items = pd.read_table(io.StringIO(items_str), index_col=0, sep=r\"\\s+\")\n",
|
||||
" bins = pd.read_table(io.StringIO(bins_str), index_col=0, sep=r\"\\s+\")\n",
|
||||
" return items, bins\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" data = create_data_model()\n",
|
||||
" num_items = len(data['items'])\n",
|
||||
" num_bins = len(data['bins'])\n",
|
||||
" items, bins = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the model.\n",
|
||||
" model = model_builder.ModelBuilder()\n",
|
||||
"\n",
|
||||
" # Variables\n",
|
||||
" # x[i, j] = 1 if item i is packed in bin j.\n",
|
||||
" x = model.new_bool_var_array(shape=[num_items, num_bins], name='x')\n",
|
||||
" items_x_bins = pd.MultiIndex.from_product(\n",
|
||||
" [items.index, bins.index], names=[\"item\", \"bin\"]\n",
|
||||
" )\n",
|
||||
" x = model.new_bool_var_series(name=\"x\", index=items_x_bins)\n",
|
||||
"\n",
|
||||
" # y[j] = 1 if bin j is used.\n",
|
||||
" y = model.new_bool_var_array(shape=[num_bins], name='y')\n",
|
||||
" y = model.new_bool_var_series(name=\"y\", index=bins.index)\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
" # Each item must be in exactly one bin.\n",
|
||||
" for i in data['items']:\n",
|
||||
" model.add(np.sum(x[i, :]) == 1)\n",
|
||||
" for unused_name, all_copies in x.groupby(\"item\"):\n",
|
||||
" model.add(x[all_copies.index].sum() == 1)\n",
|
||||
"\n",
|
||||
" # The amount packed in each bin cannot exceed its capacity.\n",
|
||||
" for j in data['bins']:\n",
|
||||
" for selected_bin in bins.index:\n",
|
||||
" items_in_bin = x.xs(selected_bin, level=\"bin\")\n",
|
||||
" model.add(\n",
|
||||
" np.dot(x[:, j], data['weights']) <= data['bin_capacity'] * y[j])\n",
|
||||
" items_in_bin.dot(items.weight)\n",
|
||||
" <= bins.loc[selected_bin].capacity * y[selected_bin]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Objective: minimize the number of bins used.\n",
|
||||
" model.minimize(np.sum(y))\n",
|
||||
" model.minimize(y.sum())\n",
|
||||
"\n",
|
||||
" # Create the solver with the CP-SAT backend, and solve the model.\n",
|
||||
" solver = model_builder.ModelSolver('sat')\n",
|
||||
" solver = model_builder.ModelSolver(\"sat\")\n",
|
||||
" status = solver.solve(model)\n",
|
||||
"\n",
|
||||
" if status == model_builder.SolveStatus.OPTIMAL:\n",
|
||||
" num_bins = 0.\n",
|
||||
" for j in data['bins']:\n",
|
||||
" if solver.value(y[j]) == 1:\n",
|
||||
" bin_items = []\n",
|
||||
" bin_weight = 0\n",
|
||||
" for i in data['items']:\n",
|
||||
" if solver.value(x[i, j]) > 0:\n",
|
||||
" bin_items.append(i)\n",
|
||||
" bin_weight += data['weights'][i]\n",
|
||||
" if bin_weight > 0:\n",
|
||||
" num_bins += 1\n",
|
||||
" print('Bin number', j)\n",
|
||||
" print(' Items packed:', bin_items)\n",
|
||||
" print(' Total weight:', bin_weight)\n",
|
||||
" print()\n",
|
||||
" print(f\"Number of bins used = {solver.objective_value}\")\n",
|
||||
"\n",
|
||||
" x_values = solver.values(x)\n",
|
||||
" y_values = solver.values(y)\n",
|
||||
" active_bins = y_values.loc[lambda x: x == 1].index\n",
|
||||
"\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 == 1].index\n",
|
||||
" for item in items_in_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",
|
||||
"\n",
|
||||
" print(f\"Total packed weight: {items.weight.sum()}\")\n",
|
||||
" print()\n",
|
||||
" print('Number of bins used:', num_bins)\n",
|
||||
" print('Time = ', solver.wall_time, ' seconds')\n",
|
||||
" print(f\"Time = {solver.wall_time} seconds\")\n",
|
||||
" else:\n",
|
||||
" print('The problem does not have an optimal solution.')\n",
|
||||
" print(\"The problem does not have an optimal solution.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solve a simple bin packing problem using a MIP solver."
|
||||
]
|
||||
},
|
||||
@@ -89,10 +90,10 @@
|
||||
" \"\"\"Create the data for the example.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" weights = [48, 30, 19, 36, 36, 27, 42, 42, 36, 24, 30]\n",
|
||||
" data['weights'] = weights\n",
|
||||
" data['items'] = list(range(len(weights)))\n",
|
||||
" data['bins'] = data['items']\n",
|
||||
" data['bin_capacity'] = 100\n",
|
||||
" data[\"weights\"] = weights\n",
|
||||
" data[\"items\"] = list(range(len(weights)))\n",
|
||||
" data[\"bins\"] = data[\"items\"]\n",
|
||||
" data[\"bin_capacity\"] = 100\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -101,7 +102,7 @@
|
||||
" data = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the mip solver with the SCIP backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('SCIP')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"SCIP\")\n",
|
||||
"\n",
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
@@ -109,52 +110,53 @@
|
||||
" # Variables\n",
|
||||
" # x[i, j] = 1 if item i is packed in bin j.\n",
|
||||
" x = {}\n",
|
||||
" for i in data['items']:\n",
|
||||
" for j in data['bins']:\n",
|
||||
" x[(i, j)] = solver.IntVar(0, 1, 'x_%i_%i' % (i, j))\n",
|
||||
" for i in data[\"items\"]:\n",
|
||||
" for j in data[\"bins\"]:\n",
|
||||
" x[(i, j)] = solver.IntVar(0, 1, \"x_%i_%i\" % (i, j))\n",
|
||||
"\n",
|
||||
" # y[j] = 1 if bin j is used.\n",
|
||||
" y = {}\n",
|
||||
" for j in data['bins']:\n",
|
||||
" y[j] = solver.IntVar(0, 1, 'y[%i]' % j)\n",
|
||||
" for j in data[\"bins\"]:\n",
|
||||
" y[j] = solver.IntVar(0, 1, \"y[%i]\" % j)\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
" # Each item must be in exactly one bin.\n",
|
||||
" for i in data['items']:\n",
|
||||
" solver.Add(sum(x[i, j] for j in data['bins']) == 1)\n",
|
||||
" for i in data[\"items\"]:\n",
|
||||
" solver.Add(sum(x[i, j] for j in data[\"bins\"]) == 1)\n",
|
||||
"\n",
|
||||
" # The amount packed in each bin cannot exceed its capacity.\n",
|
||||
" for j in data['bins']:\n",
|
||||
" for j in data[\"bins\"]:\n",
|
||||
" solver.Add(\n",
|
||||
" sum(x[(i, j)] * data['weights'][i] for i in data['items']) <= y[j] *\n",
|
||||
" data['bin_capacity'])\n",
|
||||
" sum(x[(i, j)] * data[\"weights\"][i] for i in data[\"items\"])\n",
|
||||
" <= y[j] * data[\"bin_capacity\"]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Objective: minimize the number of bins used.\n",
|
||||
" solver.Minimize(solver.Sum([y[j] for j in data['bins']]))\n",
|
||||
" solver.Minimize(solver.Sum([y[j] for j in data[\"bins\"]]))\n",
|
||||
"\n",
|
||||
" status = solver.Solve()\n",
|
||||
"\n",
|
||||
" if status == pywraplp.Solver.OPTIMAL:\n",
|
||||
" num_bins = 0\n",
|
||||
" for j in data['bins']:\n",
|
||||
" for j in data[\"bins\"]:\n",
|
||||
" if y[j].solution_value() == 1:\n",
|
||||
" bin_items = []\n",
|
||||
" bin_weight = 0\n",
|
||||
" for i in data['items']:\n",
|
||||
" for i in data[\"items\"]:\n",
|
||||
" if x[i, j].solution_value() > 0:\n",
|
||||
" bin_items.append(i)\n",
|
||||
" bin_weight += data['weights'][i]\n",
|
||||
" bin_weight += data[\"weights\"][i]\n",
|
||||
" if bin_items:\n",
|
||||
" num_bins += 1\n",
|
||||
" print('Bin number', j)\n",
|
||||
" print(' Items packed:', bin_items)\n",
|
||||
" print(' Total weight:', bin_weight)\n",
|
||||
" print(\"Bin number\", j)\n",
|
||||
" print(\" Items packed:\", bin_items)\n",
|
||||
" print(\" Total weight:\", bin_weight)\n",
|
||||
" print()\n",
|
||||
" print()\n",
|
||||
" print('Number of bins used:', num_bins)\n",
|
||||
" print('Time = ', solver.WallTime(), ' milliseconds')\n",
|
||||
" print(\"Number of bins used:\", num_bins)\n",
|
||||
" print(\"Time = \", solver.WallTime(), \" milliseconds\")\n",
|
||||
" else:\n",
|
||||
" print('The problem does not have an optimal solution.')\n",
|
||||
" print(\"The problem does not have an optimal solution.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Small example to illustrate solving a MIP problem."
|
||||
]
|
||||
},
|
||||
@@ -88,14 +89,14 @@
|
||||
"def IntegerProgrammingExample():\n",
|
||||
" \"\"\"Integer programming sample.\"\"\"\n",
|
||||
" # Create the mip solver with the SCIP backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('SCIP')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"SCIP\")\n",
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" # x, y, and z are non-negative integer variables.\n",
|
||||
" x = solver.IntVar(0.0, solver.infinity(), 'x')\n",
|
||||
" y = solver.IntVar(0.0, solver.infinity(), 'y')\n",
|
||||
" z = solver.IntVar(0.0, solver.infinity(), 'z')\n",
|
||||
" x = solver.IntVar(0.0, solver.infinity(), \"x\")\n",
|
||||
" y = solver.IntVar(0.0, solver.infinity(), \"y\")\n",
|
||||
" z = solver.IntVar(0.0, solver.infinity(), \"z\")\n",
|
||||
"\n",
|
||||
" # 2*x + 7*y + 3*z <= 50\n",
|
||||
" constraint0 = solver.Constraint(-solver.infinity(), 50)\n",
|
||||
@@ -125,11 +126,11 @@
|
||||
" # Solve the problem and print the solution.\n",
|
||||
" solver.Solve()\n",
|
||||
" # Print the objective value of the solution.\n",
|
||||
" print('Maximum objective function value = %d' % solver.Objective().Value())\n",
|
||||
" print(\"Maximum objective function value = %d\" % solver.Objective().Value())\n",
|
||||
" print()\n",
|
||||
" # Print the value of each variable in the solution.\n",
|
||||
" for variable in [x, y, z]:\n",
|
||||
" print('%s = %d' % (variable.name(), variable.solution_value()))\n",
|
||||
" print(\"%s = %d\" % (variable.name(), variable.solution_value()))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"IntegerProgrammingExample()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Linear optimization example."
|
||||
]
|
||||
},
|
||||
@@ -88,15 +89,15 @@
|
||||
"def LinearProgrammingExample():\n",
|
||||
" \"\"\"Linear programming sample.\"\"\"\n",
|
||||
" # Instantiate a Glop solver, naming it LinearExample.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('GLOP')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"GLOP\")\n",
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" # Create the two variables and let them take on any non-negative value.\n",
|
||||
" x = solver.NumVar(0, solver.infinity(), 'x')\n",
|
||||
" y = solver.NumVar(0, solver.infinity(), 'y')\n",
|
||||
" x = solver.NumVar(0, solver.infinity(), \"x\")\n",
|
||||
" y = solver.NumVar(0, solver.infinity(), \"y\")\n",
|
||||
"\n",
|
||||
" print('Number of variables =', solver.NumVariables())\n",
|
||||
" print(\"Number of variables =\", solver.NumVariables())\n",
|
||||
"\n",
|
||||
" # Constraint 0: x + 2y <= 14.\n",
|
||||
" solver.Add(x + 2 * y <= 14.0)\n",
|
||||
@@ -107,7 +108,7 @@
|
||||
" # Constraint 2: x - y <= 2.\n",
|
||||
" solver.Add(x - y <= 2.0)\n",
|
||||
"\n",
|
||||
" print('Number of constraints =', solver.NumConstraints())\n",
|
||||
" print(\"Number of constraints =\", solver.NumConstraints())\n",
|
||||
"\n",
|
||||
" # Objective function: 3x + 4y.\n",
|
||||
" solver.Maximize(3 * x + 4 * y)\n",
|
||||
@@ -116,16 +117,16 @@
|
||||
" status = solver.Solve()\n",
|
||||
"\n",
|
||||
" if status == pywraplp.Solver.OPTIMAL:\n",
|
||||
" print('Solution:')\n",
|
||||
" print('Objective value =', solver.Objective().Value())\n",
|
||||
" print('x =', x.solution_value())\n",
|
||||
" print('y =', y.solution_value())\n",
|
||||
" print(\"Solution:\")\n",
|
||||
" print(\"Objective value =\", solver.Objective().Value())\n",
|
||||
" print(\"x =\", x.solution_value())\n",
|
||||
" print(\"y =\", y.solution_value())\n",
|
||||
" else:\n",
|
||||
" print('The problem does not have an optimal solution.')\n",
|
||||
" print(\"The problem does not have an optimal solution.\")\n",
|
||||
"\n",
|
||||
" print('\\nAdvanced usage:')\n",
|
||||
" print('Problem solved in %f milliseconds' % solver.wall_time())\n",
|
||||
" print('Problem solved in %d iterations' % solver.iterations())\n",
|
||||
" print(\"\\nAdvanced usage:\")\n",
|
||||
" print(\"Problem solved in %f milliseconds\" % solver.wall_time())\n",
|
||||
" print(\"Problem solved in %d iterations\" % solver.iterations())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"LinearProgrammingExample()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"MIP example that uses a variable array."
|
||||
]
|
||||
},
|
||||
@@ -88,16 +89,16 @@
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Stores the data for the problem.\"\"\"\n",
|
||||
" data = {}\n",
|
||||
" data['constraint_coeffs'] = [\n",
|
||||
" data[\"constraint_coeffs\"] = [\n",
|
||||
" [5, 7, 9, 2, 1],\n",
|
||||
" [18, 4, -9, 10, 12],\n",
|
||||
" [4, 7, 3, 8, 5],\n",
|
||||
" [5, 13, 16, 3, -7],\n",
|
||||
" ]\n",
|
||||
" data['bounds'] = [250, 285, 211, 315]\n",
|
||||
" data['obj_coeffs'] = [7, 8, 2, 9, 6]\n",
|
||||
" data['num_vars'] = 5\n",
|
||||
" data['num_constraints'] = 4\n",
|
||||
" data[\"bounds\"] = [250, 285, 211, 315]\n",
|
||||
" data[\"obj_coeffs\"] = [7, 8, 2, 9, 6]\n",
|
||||
" data[\"num_vars\"] = 5\n",
|
||||
" data[\"num_constraints\"] = 4\n",
|
||||
" return data\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@@ -105,21 +106,21 @@
|
||||
"def main():\n",
|
||||
" data = create_data_model()\n",
|
||||
" # Create the mip solver with the SCIP backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('SCIP')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"SCIP\")\n",
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" infinity = solver.infinity()\n",
|
||||
" x = {}\n",
|
||||
" for j in range(data['num_vars']):\n",
|
||||
" x[j] = solver.IntVar(0, infinity, 'x[%i]' % j)\n",
|
||||
" print('Number of variables =', solver.NumVariables())\n",
|
||||
" for j in range(data[\"num_vars\"]):\n",
|
||||
" x[j] = solver.IntVar(0, infinity, \"x[%i]\" % j)\n",
|
||||
" print(\"Number of variables =\", solver.NumVariables())\n",
|
||||
"\n",
|
||||
" for i in range(data['num_constraints']):\n",
|
||||
" constraint = solver.RowConstraint(0, data['bounds'][i], '')\n",
|
||||
" for j in range(data['num_vars']):\n",
|
||||
" constraint.SetCoefficient(x[j], data['constraint_coeffs'][i][j])\n",
|
||||
" print('Number of constraints =', solver.NumConstraints())\n",
|
||||
" for i in range(data[\"num_constraints\"]):\n",
|
||||
" constraint = solver.RowConstraint(0, data[\"bounds\"][i], \"\")\n",
|
||||
" for j in range(data[\"num_vars\"]):\n",
|
||||
" constraint.SetCoefficient(x[j], data[\"constraint_coeffs\"][i][j])\n",
|
||||
" print(\"Number of constraints =\", solver.NumConstraints())\n",
|
||||
" # In Python, you can also set the constraints as follows.\n",
|
||||
" # for i in range(data['num_constraints']):\n",
|
||||
" # constraint_expr = \\\n",
|
||||
@@ -127,8 +128,8 @@
|
||||
" # solver.Add(sum(constraint_expr) <= data['bounds'][i])\n",
|
||||
"\n",
|
||||
" objective = solver.Objective()\n",
|
||||
" for j in range(data['num_vars']):\n",
|
||||
" objective.SetCoefficient(x[j], data['obj_coeffs'][j])\n",
|
||||
" for j in range(data[\"num_vars\"]):\n",
|
||||
" objective.SetCoefficient(x[j], data[\"obj_coeffs\"][j])\n",
|
||||
" objective.SetMaximization()\n",
|
||||
" # In Python, you can also set the objective as follows.\n",
|
||||
" # obj_expr = [data['obj_coeffs'][j] * x[j] for j in range(data['num_vars'])]\n",
|
||||
@@ -137,15 +138,15 @@
|
||||
" status = solver.Solve()\n",
|
||||
"\n",
|
||||
" if status == pywraplp.Solver.OPTIMAL:\n",
|
||||
" print('Objective value =', solver.Objective().Value())\n",
|
||||
" for j in range(data['num_vars']):\n",
|
||||
" print(x[j].name(), ' = ', x[j].solution_value())\n",
|
||||
" print(\"Objective value =\", solver.Objective().Value())\n",
|
||||
" for j in range(data[\"num_vars\"]):\n",
|
||||
" print(x[j].name(), \" = \", x[j].solution_value())\n",
|
||||
" print()\n",
|
||||
" print('Problem solved in %f milliseconds' % solver.wall_time())\n",
|
||||
" print('Problem solved in %d iterations' % solver.iterations())\n",
|
||||
" print('Problem solved in %d branch-and-bound nodes' % solver.nodes())\n",
|
||||
" print(\"Problem solved in %f milliseconds\" % solver.wall_time())\n",
|
||||
" print(\"Problem solved in %d iterations\" % solver.iterations())\n",
|
||||
" print(\"Problem solved in %d branch-and-bound nodes\" % solver.nodes())\n",
|
||||
" else:\n",
|
||||
" print('The problem does not have an optimal solution.')\n",
|
||||
" print(\"The problem does not have an optimal solution.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -88,74 +88,71 @@
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" data = {}\n",
|
||||
" data['weights'] = [\n",
|
||||
" 48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36\n",
|
||||
" ]\n",
|
||||
" data['values'] = [\n",
|
||||
" 10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25\n",
|
||||
" ]\n",
|
||||
" assert len(data['weights']) == len(data['values'])\n",
|
||||
" data['num_items'] = len(data['weights'])\n",
|
||||
" data['all_items'] = range(data['num_items'])\n",
|
||||
" data[\"weights\"] = [48, 30, 42, 36, 36, 48, 42, 42, 36, 24, 30, 30, 42, 36, 36]\n",
|
||||
" data[\"values\"] = [10, 30, 25, 50, 35, 30, 15, 40, 30, 35, 45, 10, 20, 30, 25]\n",
|
||||
" assert len(data[\"weights\"]) == len(data[\"values\"])\n",
|
||||
" data[\"num_items\"] = len(data[\"weights\"])\n",
|
||||
" data[\"all_items\"] = range(data[\"num_items\"])\n",
|
||||
"\n",
|
||||
" data['bin_capacities'] = [100, 100, 100, 100, 100]\n",
|
||||
" data['num_bins'] = len(data['bin_capacities'])\n",
|
||||
" data['all_bins'] = range(data['num_bins'])\n",
|
||||
" data[\"bin_capacities\"] = [100, 100, 100, 100, 100]\n",
|
||||
" data[\"num_bins\"] = len(data[\"bin_capacities\"])\n",
|
||||
" data[\"all_bins\"] = range(data[\"num_bins\"])\n",
|
||||
"\n",
|
||||
" # Create the mip solver with the SCIP backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('SCIP')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"SCIP\")\n",
|
||||
" if solver is None:\n",
|
||||
" print('SCIP solver unavailable.')\n",
|
||||
" print(\"SCIP solver unavailable.\")\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" # Variables.\n",
|
||||
" # x[i, b] = 1 if item i is packed in bin b.\n",
|
||||
" x = {}\n",
|
||||
" for i in data['all_items']:\n",
|
||||
" for b in data['all_bins']:\n",
|
||||
" x[i, b] = solver.BoolVar(f'x_{i}_{b}')\n",
|
||||
" for i in data[\"all_items\"]:\n",
|
||||
" for b in data[\"all_bins\"]:\n",
|
||||
" x[i, b] = solver.BoolVar(f\"x_{i}_{b}\")\n",
|
||||
"\n",
|
||||
" # Constraints.\n",
|
||||
" # Each item is assigned to at most one bin.\n",
|
||||
" for i in data['all_items']:\n",
|
||||
" solver.Add(sum(x[i, b] for b in data['all_bins']) <= 1)\n",
|
||||
" for i in data[\"all_items\"]:\n",
|
||||
" solver.Add(sum(x[i, b] for b in data[\"all_bins\"]) <= 1)\n",
|
||||
"\n",
|
||||
" # The amount packed in each bin cannot exceed its capacity.\n",
|
||||
" for b in data['all_bins']:\n",
|
||||
" for b in data[\"all_bins\"]:\n",
|
||||
" solver.Add(\n",
|
||||
" sum(x[i, b] * data['weights'][i]\n",
|
||||
" for i in data['all_items']) <= data['bin_capacities'][b])\n",
|
||||
" sum(x[i, b] * data[\"weights\"][i] for i in data[\"all_items\"])\n",
|
||||
" <= data[\"bin_capacities\"][b]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Objective.\n",
|
||||
" # Maximize total value of packed items.\n",
|
||||
" objective = solver.Objective()\n",
|
||||
" for i in data['all_items']:\n",
|
||||
" for b in data['all_bins']:\n",
|
||||
" objective.SetCoefficient(x[i, b], data['values'][i])\n",
|
||||
" for i in data[\"all_items\"]:\n",
|
||||
" for b in data[\"all_bins\"]:\n",
|
||||
" objective.SetCoefficient(x[i, b], data[\"values\"][i])\n",
|
||||
" objective.SetMaximization()\n",
|
||||
"\n",
|
||||
" status = solver.Solve()\n",
|
||||
"\n",
|
||||
" if status == pywraplp.Solver.OPTIMAL:\n",
|
||||
" print(f'Total packed value: {objective.Value()}')\n",
|
||||
" print(f\"Total packed value: {objective.Value()}\")\n",
|
||||
" total_weight = 0\n",
|
||||
" for b in data['all_bins']:\n",
|
||||
" print(f'Bin {b}')\n",
|
||||
" for b in data[\"all_bins\"]:\n",
|
||||
" print(f\"Bin {b}\")\n",
|
||||
" bin_weight = 0\n",
|
||||
" bin_value = 0\n",
|
||||
" for i in data['all_items']:\n",
|
||||
" for i in data[\"all_items\"]:\n",
|
||||
" if x[i, b].solution_value() > 0:\n",
|
||||
" print(\n",
|
||||
" f\"Item {i} weight: {data['weights'][i]} value: {data['values'][i]}\"\n",
|
||||
" )\n",
|
||||
" bin_weight += data['weights'][i]\n",
|
||||
" bin_value += data['values'][i]\n",
|
||||
" print(f'Packed bin weight: {bin_weight}')\n",
|
||||
" print(f'Packed bin value: {bin_value}\\n')\n",
|
||||
" bin_weight += data[\"weights\"][i]\n",
|
||||
" bin_value += data[\"values\"][i]\n",
|
||||
" print(f\"Packed bin weight: {bin_weight}\")\n",
|
||||
" print(f\"Packed bin value: {bin_value}\\n\")\n",
|
||||
" total_weight += bin_weight\n",
|
||||
" print(f'Total packed weight: {total_weight}')\n",
|
||||
" print(f\"Total packed weight: {total_weight}\")\n",
|
||||
" else:\n",
|
||||
" print('The problem does not have an optimal solution.')\n",
|
||||
" print(\"The problem does not have an optimal solution.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Minimal example to call the GLOP solver."
|
||||
]
|
||||
},
|
||||
@@ -87,16 +88,16 @@
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" # Create the linear solver with the GLOP backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('GLOP')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"GLOP\")\n",
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" infinity = solver.infinity()\n",
|
||||
" # Create the variables x and y.\n",
|
||||
" x = solver.NumVar(0.0, infinity, 'x')\n",
|
||||
" y = solver.NumVar(0.0, infinity, 'y')\n",
|
||||
" x = solver.NumVar(0.0, infinity, \"x\")\n",
|
||||
" y = solver.NumVar(0.0, infinity, \"y\")\n",
|
||||
"\n",
|
||||
" print('Number of variables =', solver.NumVariables())\n",
|
||||
" print(\"Number of variables =\", solver.NumVariables())\n",
|
||||
"\n",
|
||||
" # x + 7 * y <= 17.5.\n",
|
||||
" solver.Add(x + 7 * y <= 17.5)\n",
|
||||
@@ -104,25 +105,25 @@
|
||||
" # x <= 3.5.\n",
|
||||
" solver.Add(x <= 3.5)\n",
|
||||
"\n",
|
||||
" print('Number of constraints =', solver.NumConstraints())\n",
|
||||
" print(\"Number of constraints =\", solver.NumConstraints())\n",
|
||||
"\n",
|
||||
" # Maximize x + 10 * y.\n",
|
||||
" solver.Maximize(x + 10 * y)\n",
|
||||
"\n",
|
||||
" print(f'Solving with {solver.SolverVersion()}')\n",
|
||||
" print(f\"Solving with {solver.SolverVersion()}\")\n",
|
||||
" status = solver.Solve()\n",
|
||||
"\n",
|
||||
" if status == pywraplp.Solver.OPTIMAL:\n",
|
||||
" print('Solution:')\n",
|
||||
" print('Objective value =', solver.Objective().Value())\n",
|
||||
" print('x =', x.solution_value())\n",
|
||||
" print('y =', y.solution_value())\n",
|
||||
" print(\"Solution:\")\n",
|
||||
" print(\"Objective value =\", solver.Objective().Value())\n",
|
||||
" print(\"x =\", x.solution_value())\n",
|
||||
" print(\"y =\", y.solution_value())\n",
|
||||
" else:\n",
|
||||
" print('The problem does not have an optimal solution.')\n",
|
||||
" print(\"The problem does not have an optimal solution.\")\n",
|
||||
"\n",
|
||||
" print('\\nAdvanced usage:')\n",
|
||||
" print('Problem solved in %f milliseconds' % solver.wall_time())\n",
|
||||
" print('Problem solved in %d iterations' % solver.iterations())\n",
|
||||
" print(\"\\nAdvanced usage:\")\n",
|
||||
" print(\"Problem solved in %f milliseconds\" % solver.wall_time())\n",
|
||||
" print(\"Problem solved in %d iterations\" % solver.iterations())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -93,10 +93,10 @@
|
||||
" model = model_builder.ModelBuilder()\n",
|
||||
"\n",
|
||||
" # Create the variables x and y.\n",
|
||||
" x = model.new_num_var(0.0, math.inf, 'x')\n",
|
||||
" y = model.new_num_var(0.0, math.inf, 'y')\n",
|
||||
" x = model.new_num_var(0.0, math.inf, \"x\")\n",
|
||||
" y = model.new_num_var(0.0, math.inf, \"y\")\n",
|
||||
"\n",
|
||||
" print('Number of variables =', model.num_variables)\n",
|
||||
" print(\"Number of variables =\", model.num_variables)\n",
|
||||
"\n",
|
||||
" # x + 7 * y <= 17.5.\n",
|
||||
" ct = model.add(x + 7 * y <= 17.5)\n",
|
||||
@@ -104,28 +104,28 @@
|
||||
" # x <= 3.5.\n",
|
||||
" model.add(x <= 3.5)\n",
|
||||
"\n",
|
||||
" print('Number of constraints =', model.num_constraints)\n",
|
||||
" print(\"Number of constraints =\", model.num_constraints)\n",
|
||||
"\n",
|
||||
" # Maximize x + 10 * y.\n",
|
||||
" model.maximize(x + 10 * y)\n",
|
||||
"\n",
|
||||
" # Create the solver with the GLOP backend, and solve the model.\n",
|
||||
" solver = model_builder.ModelSolver('glop')\n",
|
||||
" solver = model_builder.ModelSolver(\"glop\")\n",
|
||||
" status = solver.solve(model)\n",
|
||||
"\n",
|
||||
" if status == model_builder.SolveStatus.OPTIMAL:\n",
|
||||
" print('Solution:')\n",
|
||||
" print('Objective value =', solver.objective_value)\n",
|
||||
" print('x =', solver.value(x))\n",
|
||||
" print('y =', solver.value(y))\n",
|
||||
" print(\"Solution:\")\n",
|
||||
" print(\"Objective value =\", solver.objective_value)\n",
|
||||
" print(\"x =\", solver.value(x))\n",
|
||||
" print(\"y =\", solver.value(y))\n",
|
||||
"\n",
|
||||
" print('dual_value(ct) =', solver.dual_value(ct))\n",
|
||||
" print('reduced_cost(x) =', solver.reduced_cost(x))\n",
|
||||
" print(\"dual_value(ct) =\", solver.dual_value(ct))\n",
|
||||
" print(\"reduced_cost(x) =\", solver.reduced_cost(x))\n",
|
||||
" else:\n",
|
||||
" print('The problem does not have an optimal solution.')\n",
|
||||
" print(\"The problem does not have an optimal solution.\")\n",
|
||||
"\n",
|
||||
" print('\\nAdvanced usage:')\n",
|
||||
" print('Problem solved in %f seconds' % solver.wall_time)\n",
|
||||
" print(\"\\nAdvanced usage:\")\n",
|
||||
" print(\"Problem solved in %f seconds\" % solver.wall_time)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Integer programming examples that show how to use the APIs."
|
||||
]
|
||||
},
|
||||
@@ -87,16 +88,16 @@
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" # Create the mip solver with the SCIP backend.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('SAT')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"SAT\")\n",
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" infinity = solver.infinity()\n",
|
||||
" # x and y are integer non-negative variables.\n",
|
||||
" x = solver.IntVar(0.0, infinity, 'x')\n",
|
||||
" y = solver.IntVar(0.0, infinity, 'y')\n",
|
||||
" x = solver.IntVar(0.0, infinity, \"x\")\n",
|
||||
" y = solver.IntVar(0.0, infinity, \"y\")\n",
|
||||
"\n",
|
||||
" print('Number of variables =', solver.NumVariables())\n",
|
||||
" print(\"Number of variables =\", solver.NumVariables())\n",
|
||||
"\n",
|
||||
" # x + 7 * y <= 17.5.\n",
|
||||
" solver.Add(x + 7 * y <= 17.5)\n",
|
||||
@@ -104,26 +105,26 @@
|
||||
" # x <= 3.5.\n",
|
||||
" solver.Add(x <= 3.5)\n",
|
||||
"\n",
|
||||
" print('Number of constraints =', solver.NumConstraints())\n",
|
||||
" print(\"Number of constraints =\", solver.NumConstraints())\n",
|
||||
"\n",
|
||||
" # Maximize x + 10 * y.\n",
|
||||
" solver.Maximize(x + 10 * y)\n",
|
||||
"\n",
|
||||
" print(f'Solving with {solver.SolverVersion()}')\n",
|
||||
" print(f\"Solving with {solver.SolverVersion()}\")\n",
|
||||
" status = solver.Solve()\n",
|
||||
"\n",
|
||||
" if status == pywraplp.Solver.OPTIMAL:\n",
|
||||
" print('Solution:')\n",
|
||||
" print('Objective value =', solver.Objective().Value())\n",
|
||||
" print('x =', x.solution_value())\n",
|
||||
" print('y =', y.solution_value())\n",
|
||||
" print(\"Solution:\")\n",
|
||||
" print(\"Objective value =\", solver.Objective().Value())\n",
|
||||
" print(\"x =\", x.solution_value())\n",
|
||||
" print(\"y =\", y.solution_value())\n",
|
||||
" else:\n",
|
||||
" print('The problem does not have an optimal solution.')\n",
|
||||
" print(\"The problem does not have an optimal solution.\")\n",
|
||||
"\n",
|
||||
" print('\\nAdvanced usage:')\n",
|
||||
" print('Problem solved in %f milliseconds' % solver.wall_time())\n",
|
||||
" print('Problem solved in %d iterations' % solver.iterations())\n",
|
||||
" print('Problem solved in %d branch-and-bound nodes' % solver.nodes())\n",
|
||||
" print(\"\\nAdvanced usage:\")\n",
|
||||
" print(\"Problem solved in %f milliseconds\" % solver.wall_time())\n",
|
||||
" print(\"Problem solved in %d iterations\" % solver.iterations())\n",
|
||||
" print(\"Problem solved in %d branch-and-bound nodes\" % solver.nodes())\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -93,10 +93,10 @@
|
||||
" model = model_builder.ModelBuilder()\n",
|
||||
"\n",
|
||||
" # x and y are integer non-negative variables.\n",
|
||||
" x = model.new_int_var(0.0, math.inf, 'x')\n",
|
||||
" y = model.new_int_var(0.0, math.inf, 'y')\n",
|
||||
" x = model.new_int_var(0.0, math.inf, \"x\")\n",
|
||||
" y = model.new_int_var(0.0, math.inf, \"y\")\n",
|
||||
"\n",
|
||||
" print('Number of variables =', model.num_variables)\n",
|
||||
" print(\"Number of variables =\", model.num_variables)\n",
|
||||
"\n",
|
||||
" # x + 7 * y <= 17.5.\n",
|
||||
" model.add(x + 7 * y <= 17.5)\n",
|
||||
@@ -104,25 +104,25 @@
|
||||
" # x <= 3.5.\n",
|
||||
" model.add(x <= 3.5)\n",
|
||||
"\n",
|
||||
" print('Number of constraints =', model.num_constraints)\n",
|
||||
" print(\"Number of constraints =\", model.num_constraints)\n",
|
||||
"\n",
|
||||
" # Maximize x + 10 * y.\n",
|
||||
" model.maximize(x + 10 * y)\n",
|
||||
"\n",
|
||||
" # Create the solver with the SCIP backend, and solve the model.\n",
|
||||
" solver = model_builder.ModelSolver('scip')\n",
|
||||
" solver = model_builder.ModelSolver(\"scip\")\n",
|
||||
" status = solver.solve(model)\n",
|
||||
"\n",
|
||||
" if status == model_builder.SolveStatus.OPTIMAL:\n",
|
||||
" print('Solution:')\n",
|
||||
" print('Objective value =', solver.objective_value)\n",
|
||||
" print('x =', solver.value(x))\n",
|
||||
" print('y =', solver.value(y))\n",
|
||||
" print(\"Solution:\")\n",
|
||||
" print(\"Objective value =\", solver.objective_value)\n",
|
||||
" print(\"x =\", solver.value(x))\n",
|
||||
" print(\"y =\", solver.value(y))\n",
|
||||
" else:\n",
|
||||
" print('The problem does not have an optimal solution.')\n",
|
||||
" print(\"The problem does not have an optimal solution.\")\n",
|
||||
"\n",
|
||||
" print('\\nAdvanced usage:')\n",
|
||||
" print('Problem solved in %f seconds' % solver.wall_time)\n",
|
||||
" print(\"\\nAdvanced usage:\")\n",
|
||||
" print(\"Problem solved in %f seconds\" % solver.wall_time)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -94,208 +94,111 @@
|
||||
" # Instantiate the data problem.\n",
|
||||
" # Nutrient minimums.\n",
|
||||
" nutrients = [\n",
|
||||
" ['Calories (kcal)', 3],\n",
|
||||
" ['Protein (g)', 70],\n",
|
||||
" ['Calcium (g)', 0.8],\n",
|
||||
" ['Iron (mg)', 12],\n",
|
||||
" ['Vitamin A (KIU)', 5],\n",
|
||||
" ['Vitamin B1 (mg)', 1.8],\n",
|
||||
" ['Vitamin B2 (mg)', 2.7],\n",
|
||||
" ['Niacin (mg)', 18],\n",
|
||||
" ['Vitamin C (mg)', 75],\n",
|
||||
" [\"Calories (kcal)\", 3],\n",
|
||||
" [\"Protein (g)\", 70],\n",
|
||||
" [\"Calcium (g)\", 0.8],\n",
|
||||
" [\"Iron (mg)\", 12],\n",
|
||||
" [\"Vitamin A (KIU)\", 5],\n",
|
||||
" [\"Vitamin B1 (mg)\", 1.8],\n",
|
||||
" [\"Vitamin B2 (mg)\", 2.7],\n",
|
||||
" [\"Niacin (mg)\", 18],\n",
|
||||
" [\"Vitamin C (mg)\", 75],\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" # Commodity, Unit, 1939 price (cents), Calories (kcal), Protein (g),\n",
|
||||
" # Calcium (g), Iron (mg), Vitamin A (KIU), Vitamin B1 (mg), Vitamin B2 (mg),\n",
|
||||
" # Niacin (mg), Vitamin C (mg)\n",
|
||||
" data = [\n",
|
||||
" [\n",
|
||||
" 'Wheat Flour (Enriched)', '10 lb.', 36, 44.7, 1411, 2, 365, 0, 55.4,\n",
|
||||
" 33.3, 441, 0\n",
|
||||
" ],\n",
|
||||
" ['Macaroni', '1 lb.', 14.1, 11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0],\n",
|
||||
" [\n",
|
||||
" 'Wheat Cereal (Enriched)', '28 oz.', 24.2, 11.8, 377, 14.4, 175, 0,\n",
|
||||
" 14.4, 8.8, 114, 0\n",
|
||||
" ],\n",
|
||||
" ['Corn Flakes', '8 oz.', 7.1, 11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0],\n",
|
||||
" [\n",
|
||||
" 'Corn Meal', '1 lb.', 4.6, 36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106,\n",
|
||||
" 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Hominy Grits', '24 oz.', 8.5, 28.6, 680, 0.8, 80, 0, 10.6, 1.6,\n",
|
||||
" 110, 0\n",
|
||||
" ],\n",
|
||||
" ['Rice', '1 lb.', 7.5, 21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0],\n",
|
||||
" ['Rolled Oats', '1 lb.', 7.1, 25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0],\n",
|
||||
" [\n",
|
||||
" 'White Bread (Enriched)', '1 lb.', 7.9, 15.0, 488, 2.5, 115, 0,\n",
|
||||
" 13.8, 8.5, 126, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Whole Wheat Bread', '1 lb.', 9.1, 12.2, 484, 2.7, 125, 0, 13.9,\n",
|
||||
" 6.4, 160, 0\n",
|
||||
" ],\n",
|
||||
" ['Rye Bread', '1 lb.', 9.1, 12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0],\n",
|
||||
" ['Pound Cake', '1 lb.', 24.8, 8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0],\n",
|
||||
" ['Soda Crackers', '1 lb.', 15.1, 12.5, 288, 0.5, 50, 0, 0, 0, 0, 0],\n",
|
||||
" ['Milk', '1 qt.', 11, 6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177],\n",
|
||||
" [\n",
|
||||
" 'Evaporated Milk (can)', '14.5 oz.', 6.7, 8.4, 422, 15.1, 9, 26, 3,\n",
|
||||
" 23.5, 11, 60\n",
|
||||
" ],\n",
|
||||
" ['Butter', '1 lb.', 30.8, 10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0],\n",
|
||||
" ['Oleomargarine', '1 lb.', 16.1, 20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0],\n",
|
||||
" ['Eggs', '1 doz.', 32.6, 2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0],\n",
|
||||
" [\n",
|
||||
" 'Cheese (Cheddar)', '1 lb.', 24.2, 7.4, 448, 16.4, 19, 28.1, 0.8,\n",
|
||||
" 10.3, 4, 0\n",
|
||||
" ],\n",
|
||||
" ['Cream', '1/2 pt.', 14.1, 3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17],\n",
|
||||
" [\n",
|
||||
" 'Peanut Butter', '1 lb.', 17.9, 15.7, 661, 1.0, 48, 0, 9.6, 8.1,\n",
|
||||
" 471, 0\n",
|
||||
" ],\n",
|
||||
" ['Mayonnaise', '1/2 pt.', 16.7, 8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0],\n",
|
||||
" ['Crisco', '1 lb.', 20.3, 20.1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" ['Lard', '1 lb.', 9.8, 41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0],\n",
|
||||
" [\n",
|
||||
" 'Sirloin Steak', '1 lb.', 39.6, 2.9, 166, 0.1, 34, 0.2, 2.1, 2.9,\n",
|
||||
" 69, 0\n",
|
||||
" ],\n",
|
||||
" ['Round Steak', '1 lb.', 36.4, 2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0],\n",
|
||||
" ['Rib Roast', '1 lb.', 29.2, 3.4, 213, 0.1, 33, 0, 0, 2, 0, 0],\n",
|
||||
" ['Chuck Roast', '1 lb.', 22.6, 3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0],\n",
|
||||
" ['Plate', '1 lb.', 14.6, 8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0],\n",
|
||||
" [\n",
|
||||
" 'Liver (Beef)', '1 lb.', 26.8, 2.2, 333, 0.2, 139, 169.2, 6.4, 50.8,\n",
|
||||
" 316, 525\n",
|
||||
" ],\n",
|
||||
" ['Leg of Lamb', '1 lb.', 27.6, 3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0],\n",
|
||||
" [\n",
|
||||
" 'Lamb Chops (Rib)', '1 lb.', 36.6, 3.3, 140, 0.1, 15, 0, 1.7, 2.7,\n",
|
||||
" 54, 0\n",
|
||||
" ],\n",
|
||||
" ['Pork Chops', '1 lb.', 30.7, 3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0],\n",
|
||||
" [\n",
|
||||
" 'Pork Loin Roast', '1 lb.', 24.2, 4.4, 249, 0.3, 37, 0, 18.2, 3.6,\n",
|
||||
" 79, 0\n",
|
||||
" ],\n",
|
||||
" ['Bacon', '1 lb.', 25.6, 10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0],\n",
|
||||
" ['Ham, smoked', '1 lb.', 27.4, 6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0],\n",
|
||||
" ['Salt Pork', '1 lb.', 16, 18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0],\n",
|
||||
" [\n",
|
||||
" 'Roasting Chicken', '1 lb.', 30.3, 1.8, 184, 0.1, 30, 0.1, 0.9, 1.8,\n",
|
||||
" 68, 46\n",
|
||||
" ],\n",
|
||||
" ['Veal Cutlets', '1 lb.', 42.3, 1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0],\n",
|
||||
" [\n",
|
||||
" 'Salmon, Pink (can)', '16 oz.', 13, 5.8, 705, 6.8, 45, 3.5, 1, 4.9,\n",
|
||||
" 209, 0\n",
|
||||
" ],\n",
|
||||
" ['Apples', '1 lb.', 4.4, 5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544],\n",
|
||||
" ['Bananas', '1 lb.', 6.1, 4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498],\n",
|
||||
" ['Lemons', '1 doz.', 26, 1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952],\n",
|
||||
" ['Oranges', '1 doz.', 30.9, 2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998],\n",
|
||||
" ['Green Beans', '1 lb.', 7.1, 2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862],\n",
|
||||
" ['Cabbage', '1 lb.', 3.7, 2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369],\n",
|
||||
" ['Carrots', '1 bunch', 4.7, 2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608],\n",
|
||||
" ['Celery', '1 stalk', 7.3, 0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313],\n",
|
||||
" ['Lettuce', '1 head', 8.2, 0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449],\n",
|
||||
" ['Onions', '1 lb.', 3.6, 5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184],\n",
|
||||
" [\n",
|
||||
" 'Potatoes', '15 lb.', 34, 14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198,\n",
|
||||
" 2522\n",
|
||||
" ],\n",
|
||||
" ['Spinach', '1 lb.', 8.1, 1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755],\n",
|
||||
" [\n",
|
||||
" 'Sweet Potatoes', '1 lb.', 5.1, 9.6, 138, 2.7, 54, 290.7, 8.4, 5.4,\n",
|
||||
" 83, 1912\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Peaches (can)', 'No. 2 1/2', 16.8, 3.7, 20, 0.4, 10, 21.5, 0.5, 1,\n",
|
||||
" 31, 196\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Pears (can)', 'No. 2 1/2', 20.4, 3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5,\n",
|
||||
" 81\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Pineapple (can)', 'No. 2 1/2', 21.3, 2.4, 16, 0.4, 8, 2, 2.8, 0.8,\n",
|
||||
" 7, 399\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Asparagus (can)', 'No. 2', 27.7, 0.4, 33, 0.3, 12, 16.3, 1.4, 2.1,\n",
|
||||
" 17, 272\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Green Beans (can)', 'No. 2', 10, 1.0, 54, 2, 65, 53.9, 1.6, 4.3,\n",
|
||||
" 32, 431\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Pork and Beans (can)', '16 oz.', 7.1, 7.5, 364, 4, 134, 3.5, 8.3,\n",
|
||||
" 7.7, 56, 0\n",
|
||||
" ],\n",
|
||||
" ['Corn (can)', 'No. 2', 10.4, 5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218],\n",
|
||||
" [\n",
|
||||
" 'Peas (can)', 'No. 2', 13.8, 2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37,\n",
|
||||
" 370\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Tomatoes (can)', 'No. 2', 8.6, 1.3, 63, 0.7, 38, 53.2, 3.4, 2.5,\n",
|
||||
" 36, 1253\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Tomato Soup (can)', '10 1/2 oz.', 7.6, 1.6, 71, 0.6, 43, 57.9, 3.5,\n",
|
||||
" 2.4, 67, 862\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Peaches, Dried', '1 lb.', 15.7, 8.5, 87, 1.7, 173, 86.8, 1.2, 4.3,\n",
|
||||
" 55, 57\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Prunes, Dried', '1 lb.', 9, 12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65,\n",
|
||||
" 257\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Raisins, Dried', '15 oz.', 9.4, 13.5, 104, 2.5, 136, 4.5, 6.3, 1.4,\n",
|
||||
" 24, 136\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Peas, Dried', '1 lb.', 7.9, 20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4,\n",
|
||||
" 162, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Lima Beans, Dried', '1 lb.', 8.9, 17.4, 1055, 3.7, 459, 5.1, 26.9,\n",
|
||||
" 38.2, 93, 0\n",
|
||||
" ],\n",
|
||||
" [\n",
|
||||
" 'Navy Beans, Dried', '1 lb.', 5.9, 26.9, 1691, 11.4, 792, 0, 38.4,\n",
|
||||
" 24.6, 217, 0\n",
|
||||
" ],\n",
|
||||
" ['Coffee', '1 lb.', 22.4, 0, 0, 0, 0, 0, 4, 5.1, 50, 0],\n",
|
||||
" ['Tea', '1/4 lb.', 17.4, 0, 0, 0, 0, 0, 0, 2.3, 42, 0],\n",
|
||||
" ['Cocoa', '8 oz.', 8.6, 8.7, 237, 3, 72, 0, 2, 11.9, 40, 0],\n",
|
||||
" ['Chocolate', '8 oz.', 16.2, 8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0],\n",
|
||||
" ['Sugar', '10 lb.', 51.7, 34.9, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" ['Corn Syrup', '24 oz.', 13.7, 14.7, 0, 0.5, 74, 0, 0, 0, 5, 0],\n",
|
||||
" ['Molasses', '18 oz.', 13.6, 9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0],\n",
|
||||
" [\n",
|
||||
" 'Strawberry Preserves', '1 lb.', 20.5, 6.4, 11, 0.4, 7, 0.2, 0.2,\n",
|
||||
" 0.4, 3, 0\n",
|
||||
" ],\n",
|
||||
" # fmt: off\n",
|
||||
" ['Wheat Flour (Enriched)', '10 lb.', 36, 44.7, 1411, 2, 365, 0, 55.4, 33.3, 441, 0],\n",
|
||||
" ['Macaroni', '1 lb.', 14.1, 11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0],\n",
|
||||
" ['Wheat Cereal (Enriched)', '28 oz.', 24.2, 11.8, 377, 14.4, 175, 0, 14.4, 8.8, 114, 0],\n",
|
||||
" ['Corn Flakes', '8 oz.', 7.1, 11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0],\n",
|
||||
" ['Corn Meal', '1 lb.', 4.6, 36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106, 0],\n",
|
||||
" ['Hominy Grits', '24 oz.', 8.5, 28.6, 680, 0.8, 80, 0, 10.6, 1.6, 110, 0],\n",
|
||||
" ['Rice', '1 lb.', 7.5, 21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0],\n",
|
||||
" ['Rolled Oats', '1 lb.', 7.1, 25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0],\n",
|
||||
" ['White Bread (Enriched)', '1 lb.', 7.9, 15.0, 488, 2.5, 115, 0, 13.8, 8.5, 126, 0],\n",
|
||||
" ['Whole Wheat Bread', '1 lb.', 9.1, 12.2, 484, 2.7, 125, 0, 13.9, 6.4, 160, 0],\n",
|
||||
" ['Rye Bread', '1 lb.', 9.1, 12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0],\n",
|
||||
" ['Pound Cake', '1 lb.', 24.8, 8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0],\n",
|
||||
" ['Soda Crackers', '1 lb.', 15.1, 12.5, 288, 0.5, 50, 0, 0, 0, 0, 0],\n",
|
||||
" ['Milk', '1 qt.', 11, 6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177],\n",
|
||||
" ['Evaporated Milk (can)', '14.5 oz.', 6.7, 8.4, 422, 15.1, 9, 26, 3, 23.5, 11, 60],\n",
|
||||
" ['Butter', '1 lb.', 30.8, 10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0],\n",
|
||||
" ['Oleomargarine', '1 lb.', 16.1, 20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0],\n",
|
||||
" ['Eggs', '1 doz.', 32.6, 2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0],\n",
|
||||
" ['Cheese (Cheddar)', '1 lb.', 24.2, 7.4, 448, 16.4, 19, 28.1, 0.8, 10.3, 4, 0],\n",
|
||||
" ['Cream', '1/2 pt.', 14.1, 3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17],\n",
|
||||
" ['Peanut Butter', '1 lb.', 17.9, 15.7, 661, 1.0, 48, 0, 9.6, 8.1, 471, 0],\n",
|
||||
" ['Mayonnaise', '1/2 pt.', 16.7, 8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0],\n",
|
||||
" ['Crisco', '1 lb.', 20.3, 20.1, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" ['Lard', '1 lb.', 9.8, 41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0],\n",
|
||||
" ['Sirloin Steak', '1 lb.', 39.6, 2.9, 166, 0.1, 34, 0.2, 2.1, 2.9, 69, 0],\n",
|
||||
" ['Round Steak', '1 lb.', 36.4, 2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0],\n",
|
||||
" ['Rib Roast', '1 lb.', 29.2, 3.4, 213, 0.1, 33, 0, 0, 2, 0, 0],\n",
|
||||
" ['Chuck Roast', '1 lb.', 22.6, 3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0],\n",
|
||||
" ['Plate', '1 lb.', 14.6, 8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0],\n",
|
||||
" ['Liver (Beef)', '1 lb.', 26.8, 2.2, 333, 0.2, 139, 169.2, 6.4, 50.8, 316, 525],\n",
|
||||
" ['Leg of Lamb', '1 lb.', 27.6, 3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0],\n",
|
||||
" ['Lamb Chops (Rib)', '1 lb.', 36.6, 3.3, 140, 0.1, 15, 0, 1.7, 2.7, 54, 0],\n",
|
||||
" ['Pork Chops', '1 lb.', 30.7, 3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0],\n",
|
||||
" ['Pork Loin Roast', '1 lb.', 24.2, 4.4, 249, 0.3, 37, 0, 18.2, 3.6, 79, 0],\n",
|
||||
" ['Bacon', '1 lb.', 25.6, 10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0],\n",
|
||||
" ['Ham, smoked', '1 lb.', 27.4, 6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0],\n",
|
||||
" ['Salt Pork', '1 lb.', 16, 18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0],\n",
|
||||
" ['Roasting Chicken', '1 lb.', 30.3, 1.8, 184, 0.1, 30, 0.1, 0.9, 1.8, 68, 46],\n",
|
||||
" ['Veal Cutlets', '1 lb.', 42.3, 1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0],\n",
|
||||
" ['Salmon, Pink (can)', '16 oz.', 13, 5.8, 705, 6.8, 45, 3.5, 1, 4.9, 209, 0],\n",
|
||||
" ['Apples', '1 lb.', 4.4, 5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544],\n",
|
||||
" ['Bananas', '1 lb.', 6.1, 4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498],\n",
|
||||
" ['Lemons', '1 doz.', 26, 1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952],\n",
|
||||
" ['Oranges', '1 doz.', 30.9, 2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998],\n",
|
||||
" ['Green Beans', '1 lb.', 7.1, 2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862],\n",
|
||||
" ['Cabbage', '1 lb.', 3.7, 2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369],\n",
|
||||
" ['Carrots', '1 bunch', 4.7, 2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608],\n",
|
||||
" ['Celery', '1 stalk', 7.3, 0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313],\n",
|
||||
" ['Lettuce', '1 head', 8.2, 0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449],\n",
|
||||
" ['Onions', '1 lb.', 3.6, 5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184],\n",
|
||||
" ['Potatoes', '15 lb.', 34, 14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198, 2522],\n",
|
||||
" ['Spinach', '1 lb.', 8.1, 1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755],\n",
|
||||
" ['Sweet Potatoes', '1 lb.', 5.1, 9.6, 138, 2.7, 54, 290.7, 8.4, 5.4, 83, 1912],\n",
|
||||
" ['Peaches (can)', 'No. 2 1/2', 16.8, 3.7, 20, 0.4, 10, 21.5, 0.5, 1, 31, 196],\n",
|
||||
" ['Pears (can)', 'No. 2 1/2', 20.4, 3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5, 81],\n",
|
||||
" ['Pineapple (can)', 'No. 2 1/2', 21.3, 2.4, 16, 0.4, 8, 2, 2.8, 0.8, 7, 399],\n",
|
||||
" ['Asparagus (can)', 'No. 2', 27.7, 0.4, 33, 0.3, 12, 16.3, 1.4, 2.1, 17, 272],\n",
|
||||
" ['Green Beans (can)', 'No. 2', 10, 1.0, 54, 2, 65, 53.9, 1.6, 4.3, 32, 431],\n",
|
||||
" ['Pork and Beans (can)', '16 oz.', 7.1, 7.5, 364, 4, 134, 3.5, 8.3, 7.7, 56, 0],\n",
|
||||
" ['Corn (can)', 'No. 2', 10.4, 5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218],\n",
|
||||
" ['Peas (can)', 'No. 2', 13.8, 2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37, 370],\n",
|
||||
" ['Tomatoes (can)', 'No. 2', 8.6, 1.3, 63, 0.7, 38, 53.2, 3.4, 2.5, 36, 1253],\n",
|
||||
" ['Tomato Soup (can)', '10 1/2 oz.', 7.6, 1.6, 71, 0.6, 43, 57.9, 3.5, 2.4, 67, 862],\n",
|
||||
" ['Peaches, Dried', '1 lb.', 15.7, 8.5, 87, 1.7, 173, 86.8, 1.2, 4.3, 55, 57],\n",
|
||||
" ['Prunes, Dried', '1 lb.', 9, 12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65, 257],\n",
|
||||
" ['Raisins, Dried', '15 oz.', 9.4, 13.5, 104, 2.5, 136, 4.5, 6.3, 1.4, 24, 136],\n",
|
||||
" ['Peas, Dried', '1 lb.', 7.9, 20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4, 162, 0],\n",
|
||||
" ['Lima Beans, Dried', '1 lb.', 8.9, 17.4, 1055, 3.7, 459, 5.1, 26.9, 38.2, 93, 0],\n",
|
||||
" ['Navy Beans, Dried', '1 lb.', 5.9, 26.9, 1691, 11.4, 792, 0, 38.4, 24.6, 217, 0],\n",
|
||||
" ['Coffee', '1 lb.', 22.4, 0, 0, 0, 0, 0, 4, 5.1, 50, 0],\n",
|
||||
" ['Tea', '1/4 lb.', 17.4, 0, 0, 0, 0, 0, 0, 2.3, 42, 0],\n",
|
||||
" ['Cocoa', '8 oz.', 8.6, 8.7, 237, 3, 72, 0, 2, 11.9, 40, 0],\n",
|
||||
" ['Chocolate', '8 oz.', 16.2, 8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0],\n",
|
||||
" ['Sugar', '10 lb.', 51.7, 34.9, 0, 0, 0, 0, 0, 0, 0, 0],\n",
|
||||
" ['Corn Syrup', '24 oz.', 13.7, 14.7, 0, 0.5, 74, 0, 0, 0, 5, 0],\n",
|
||||
" ['Molasses', '18 oz.', 13.6, 9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0],\n",
|
||||
" ['Strawberry Preserves', '1 lb.', 20.5, 6.4, 11, 0.4, 7, 0.2, 0.2, 0.4, 3, 0],\n",
|
||||
" # fmt: on\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" # Instantiate a Glop solver and naming it.\n",
|
||||
" solver = pywraplp.Solver.CreateSolver('GLOP')\n",
|
||||
" solver = pywraplp.Solver.CreateSolver(\"GLOP\")\n",
|
||||
" if not solver:\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" # Declare an array to hold our variables.\n",
|
||||
" foods = [solver.NumVar(0.0, solver.infinity(), item[0]) for item in data]\n",
|
||||
"\n",
|
||||
" print('Number of variables =', solver.NumVariables())\n",
|
||||
" print(\"Number of variables =\", solver.NumVariables())\n",
|
||||
"\n",
|
||||
" # Create the constraints, one per nutrient.\n",
|
||||
" constraints = []\n",
|
||||
@@ -304,7 +207,7 @@
|
||||
" for j, item in enumerate(data):\n",
|
||||
" constraints[i].SetCoefficient(foods[j], item[i + 3])\n",
|
||||
"\n",
|
||||
" print('Number of constraints =', solver.NumConstraints())\n",
|
||||
" print(\"Number of constraints =\", solver.NumConstraints())\n",
|
||||
"\n",
|
||||
" # Objective function: Minimize the sum of (price-normalized) foods.\n",
|
||||
" objective = solver.Objective()\n",
|
||||
@@ -316,31 +219,32 @@
|
||||
"\n",
|
||||
" # Check that the problem has an optimal solution.\n",
|
||||
" if status != solver.OPTIMAL:\n",
|
||||
" print('The problem does not have an optimal solution!')\n",
|
||||
" print(\"The problem does not have an optimal solution!\")\n",
|
||||
" if status == solver.FEASIBLE:\n",
|
||||
" print('A potentially suboptimal solution was found.')\n",
|
||||
" print(\"A potentially suboptimal solution was found.\")\n",
|
||||
" else:\n",
|
||||
" print('The solver could not solve the problem.')\n",
|
||||
" print(\"The solver could not solve the problem.\")\n",
|
||||
" exit(1)\n",
|
||||
"\n",
|
||||
" # Display the amounts (in dollars) to purchase of each food.\n",
|
||||
" nutrients_result = [0] * len(nutrients)\n",
|
||||
" print('\\nAnnual Foods:')\n",
|
||||
" print(\"\\nAnnual Foods:\")\n",
|
||||
" for i, food in enumerate(foods):\n",
|
||||
" if food.solution_value() > 0.0:\n",
|
||||
" print('{}: ${}'.format(data[i][0], 365. * food.solution_value()))\n",
|
||||
" print(\"{}: ${}\".format(data[i][0], 365.0 * food.solution_value()))\n",
|
||||
" for j, _ in enumerate(nutrients):\n",
|
||||
" nutrients_result[j] += data[i][j + 3] * food.solution_value()\n",
|
||||
" print('\\nOptimal annual price: ${:.4f}'.format(365. * objective.Value()))\n",
|
||||
" print(\"\\nOptimal annual price: ${:.4f}\".format(365.0 * objective.Value()))\n",
|
||||
"\n",
|
||||
" print('\\nNutrients per day:')\n",
|
||||
" print(\"\\nNutrients per day:\")\n",
|
||||
" for i, nutrient in enumerate(nutrients):\n",
|
||||
" print('{}: {:.2f} (min {})'.format(nutrient[0], nutrients_result[i],\n",
|
||||
" nutrient[1]))\n",
|
||||
" print(\n",
|
||||
" \"{}: {:.2f} (min {})\".format(nutrient[0], nutrients_result[i], nutrient[1])\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" print('\\nAdvanced usage:')\n",
|
||||
" print('Problem solved in ', solver.wall_time(), ' milliseconds')\n",
|
||||
" print('Problem solved in ', solver.iterations(), ' iterations')\n",
|
||||
" print(\"\\nAdvanced usage:\")\n",
|
||||
" print(\"Problem solved in \", solver.wall_time(), \" milliseconds\")\n",
|
||||
" print(\"Problem solved in \", solver.iterations(), \" iterations\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solves a simple LP using PDLP's direct Python API.\n",
|
||||
"\n",
|
||||
"Note: The direct API is generally for advanced use cases. It is matrix-based,\n",
|
||||
@@ -93,24 +94,24 @@
|
||||
"\n",
|
||||
"from ortools.pdlp import solve_log_pb2\n",
|
||||
"from ortools.pdlp import solvers_pb2\n",
|
||||
"from ortools.pdlp.python import pywrap_pdlp\n",
|
||||
"from ortools.init import pywrapinit\n",
|
||||
"from ortools.pdlp.python import pdlp\n",
|
||||
"from ortools.init.python import init\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def simple_lp() -> pywrap_pdlp.QuadraticProgram:\n",
|
||||
"def simple_lp() -> pdlp.QuadraticProgram:\n",
|
||||
" \"\"\"Returns a small LP.\n",
|
||||
"\n",
|
||||
" min 5.5 x_0 - 2 x_1 - x_2 + x_3 - 14 s.t.\n",
|
||||
" 2 x_0 + x_1 + x_2 + 2 x_3 = 12\n",
|
||||
" x_0 + x_2 <= 7\n",
|
||||
" 4 x_0 >= -4\n",
|
||||
" -1 <= 1.5 x_2 - x_3 <= 1\n",
|
||||
" -infinity <= x_0 <= infinity\n",
|
||||
" -2 <= x_1 <= infinity\n",
|
||||
" -infinity <= x_2 <= 6\n",
|
||||
" 2.5 <= x_3 <= 3.5\n",
|
||||
" \"\"\"\n",
|
||||
" lp = pywrap_pdlp.QuadraticProgram()\n",
|
||||
" min 5.5 x_0 - 2 x_1 - x_2 + x_3 - 14 s.t.\n",
|
||||
" 2 x_0 + x_1 + x_2 + 2 x_3 = 12\n",
|
||||
" x_0 + x_2 <= 7\n",
|
||||
" 4 x_0 >= -4\n",
|
||||
" -1 <= 1.5 x_2 - x_3 <= 1\n",
|
||||
" -infinity <= x_0 <= infinity\n",
|
||||
" -2 <= x_1 <= infinity\n",
|
||||
" -infinity <= x_2 <= 6\n",
|
||||
" 2.5 <= x_3 <= 3.5\n",
|
||||
" \"\"\"\n",
|
||||
" lp = pdlp.QuadraticProgram()\n",
|
||||
" lp.objective_offset = -14\n",
|
||||
" lp.objective_vector = [5.5, -2, -1, 1]\n",
|
||||
" lp.constraint_lower_bounds = [12, -np.inf, -4, -1]\n",
|
||||
@@ -120,8 +121,9 @@
|
||||
" # Most use cases should initialize the sparse constraint matrix without\n",
|
||||
" # constructing a dense matrix first! We use a np.array here for convenience\n",
|
||||
" # only.\n",
|
||||
" constraint_matrix = np.array([[2, 1, 1, 2], [1, 0, 1, 0], [4, 0, 0, 0],\n",
|
||||
" [0, 0, 1.5, -1]])\n",
|
||||
" constraint_matrix = np.array(\n",
|
||||
" [[2, 1, 1, 2], [1, 0, 1, 0], [4, 0, 0, 0], [0, 0, 1.5, -1]]\n",
|
||||
" )\n",
|
||||
" lp.constraint_matrix = scipy.sparse.csc_matrix(constraint_matrix)\n",
|
||||
" return lp\n",
|
||||
"\n",
|
||||
@@ -140,41 +142,41 @@
|
||||
"\n",
|
||||
" # Call the main solve function. Note that a quirk of the pywrap11 API forces\n",
|
||||
" # us to serialize the `params` and deserialize the `solve_log` proto messages.\n",
|
||||
" result = pywrap_pdlp.primal_dual_hybrid_gradient(simple_lp(),\n",
|
||||
" params.SerializeToString())\n",
|
||||
" result = pdlp.primal_dual_hybrid_gradient(simple_lp(), params.SerializeToString())\n",
|
||||
" solve_log = solve_log_pb2.SolveLog.FromString(result.solve_log_str)\n",
|
||||
"\n",
|
||||
" if solve_log.termination_reason == solve_log_pb2.TERMINATION_REASON_OPTIMAL:\n",
|
||||
" print('Solve successful')\n",
|
||||
" print(\"Solve successful\")\n",
|
||||
" else:\n",
|
||||
" print(\n",
|
||||
" 'Solve not successful. Status:',\n",
|
||||
" solve_log_pb2.TerminationReason.Name(solve_log.termination_reason))\n",
|
||||
" \"Solve not successful. Status:\",\n",
|
||||
" solve_log_pb2.TerminationReason.Name(solve_log.termination_reason),\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Solutions vectors are always returned. *However*, their interpretation\n",
|
||||
" # depends on termination_reason! See primal_dual_hybrid_gradient.h for more\n",
|
||||
" # details on what the vectors mean if termination_reason is not\n",
|
||||
" # TERMINATION_REASON_OPTIMAL.\n",
|
||||
" print('Primal solution:', result.primal_solution)\n",
|
||||
" print('Dual solution:', result.dual_solution)\n",
|
||||
" print('Reduced costs:', result.reduced_costs)\n",
|
||||
" print(\"Primal solution:\", result.primal_solution)\n",
|
||||
" print(\"Dual solution:\", result.dual_solution)\n",
|
||||
" print(\"Reduced costs:\", result.reduced_costs)\n",
|
||||
"\n",
|
||||
" solution_type = solve_log.solution_type\n",
|
||||
" print('Solution type:', solve_log_pb2.PointType.Name(solution_type))\n",
|
||||
" print(\"Solution type:\", solve_log_pb2.PointType.Name(solution_type))\n",
|
||||
" for ci in solve_log.solution_stats.convergence_information:\n",
|
||||
" if ci.candidate_type == solution_type:\n",
|
||||
" print('Primal objective:', ci.primal_objective)\n",
|
||||
" print('Dual objective:', ci.dual_objective)\n",
|
||||
" print(\"Primal objective:\", ci.primal_objective)\n",
|
||||
" print(\"Dual objective:\", ci.dual_objective)\n",
|
||||
"\n",
|
||||
" print('Iterations:', solve_log.iteration_count)\n",
|
||||
" print('Solve time (sec):', solve_log.solve_time_sec)\n",
|
||||
" print(\"Iterations:\", solve_log.iteration_count)\n",
|
||||
" print(\"Solve time (sec):\", solve_log.solve_time_sec)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"pywrapinit.CppBridge.InitLogging('simple_pdlp_program.py')\n",
|
||||
"cpp_flags = pywrapinit.CppFlags()\n",
|
||||
"cpp_flags.logtostderr = True\n",
|
||||
"init.CppBridge.init_logging(\"simple_pdlp_program.py\")\n",
|
||||
"cpp_flags = init.CppFlags()\n",
|
||||
"cpp_flags.stderrthreshold = 0\n",
|
||||
"cpp_flags.log_prefix = False\n",
|
||||
"pywrapinit.CppBridge.SetFlags(cpp_flags)\n",
|
||||
"init.CppBridge.set_flags(cpp_flags)\n",
|
||||
"main()\n",
|
||||
"\n"
|
||||
]
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
" x = {}\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" x[worker, task] = model.NewBoolVar(f'x[{worker},{task}]')\n",
|
||||
" x[worker, task] = model.NewBoolVar(f\"x[{worker},{task}]\")\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
" # Each worker is assigned to at most one task.\n",
|
||||
@@ -151,12 +151,11 @@
|
||||
" # Create variables for each worker, indicating whether they work on some task.\n",
|
||||
" work = {}\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" work[worker] = model.NewBoolVar(f'work[{worker}]')\n",
|
||||
" work[worker] = model.NewBoolVar(f\"work[{worker}]\")\n",
|
||||
"\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" model.Add(work[worker] == sum(\n",
|
||||
" x[worker, task] for task in range(num_tasks)))\n",
|
||||
" model.Add(work[worker] == sum(x[worker, task] for task in range(num_tasks)))\n",
|
||||
"\n",
|
||||
" # Define the allowed groups of worders\n",
|
||||
" model.AddAllowedAssignments([work[0], work[1], work[2], work[3]], group1)\n",
|
||||
@@ -176,14 +175,16 @@
|
||||
"\n",
|
||||
" # Print solution.\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" print(f'Total cost = {solver.ObjectiveValue()}\\n')\n",
|
||||
" print(f\"Total cost = {solver.ObjectiveValue()}\\n\")\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" if solver.BooleanValue(x[worker, task]):\n",
|
||||
" print(f'Worker {worker} assigned to task {task}.' +\n",
|
||||
" f' Cost = {costs[worker][task]}')\n",
|
||||
" print(\n",
|
||||
" f\"Worker {worker} assigned to task {task}.\"\n",
|
||||
" + f\" Cost = {costs[worker][task]}\"\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('No solution found.')\n",
|
||||
" print(\"No solution found.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Solve a simple assignment problem."
|
||||
]
|
||||
},
|
||||
@@ -82,47 +83,58 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import io\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" # Data\n",
|
||||
" costs = [\n",
|
||||
" [90, 80, 75, 70],\n",
|
||||
" [35, 85, 55, 65],\n",
|
||||
" [125, 95, 90, 95],\n",
|
||||
" [45, 110, 95, 115],\n",
|
||||
" [50, 100, 90, 100],\n",
|
||||
" ]\n",
|
||||
" num_workers = len(costs)\n",
|
||||
" num_tasks = len(costs[0])\n",
|
||||
" data_str = \"\"\"\n",
|
||||
" worker task cost\n",
|
||||
" w1 t1 90\n",
|
||||
" w1 t2 80\n",
|
||||
" w1 t3 75\n",
|
||||
" w1 t4 70\n",
|
||||
" w2 t1 35\n",
|
||||
" w2 t2 85\n",
|
||||
" w2 t3 55\n",
|
||||
" w2 t4 65\n",
|
||||
" w3 t1 125\n",
|
||||
" w3 t2 95\n",
|
||||
" w3 t3 90\n",
|
||||
" w3 t4 95\n",
|
||||
" w4 t1 45\n",
|
||||
" w4 t2 110\n",
|
||||
" w4 t3 95\n",
|
||||
" w4 t4 115\n",
|
||||
" w5 t1 50\n",
|
||||
" w5 t2 110\n",
|
||||
" w5 t3 90\n",
|
||||
" w5 t4 100\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" data = pd.read_table(io.StringIO(data_str), sep=r\"\\s+\")\n",
|
||||
"\n",
|
||||
" # Model\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
" # Variables\n",
|
||||
" x = []\n",
|
||||
" for i in range(num_workers):\n",
|
||||
" t = []\n",
|
||||
" for j in range(num_tasks):\n",
|
||||
" t.append(model.NewBoolVar(f'x[{i},{j}]'))\n",
|
||||
" x.append(t)\n",
|
||||
" x = model.NewBoolVarSeries(name=\"x\", index=data.index)\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
" # Each worker is assigned to at most one task.\n",
|
||||
" for i in range(num_workers):\n",
|
||||
" model.AddAtMostOne(x[i][j] for j in range(num_tasks))\n",
|
||||
" for unused_name, tasks in data.groupby(\"worker\"):\n",
|
||||
" model.AddAtMostOne(x[tasks.index])\n",
|
||||
"\n",
|
||||
" # Each task is assigned to exactly one worker.\n",
|
||||
" for j in range(num_tasks):\n",
|
||||
" model.AddExactlyOne(x[i][j] for i in range(num_workers))\n",
|
||||
" for unused_name, workers in data.groupby(\"task\"):\n",
|
||||
" model.AddExactlyOne(x[workers.index])\n",
|
||||
"\n",
|
||||
" # Objective\n",
|
||||
" objective_terms = []\n",
|
||||
" for i in range(num_workers):\n",
|
||||
" for j in range(num_tasks):\n",
|
||||
" objective_terms.append(costs[i][j] * x[i][j])\n",
|
||||
" model.Minimize(sum(objective_terms))\n",
|
||||
" model.Minimize(data.cost.dot(x))\n",
|
||||
"\n",
|
||||
" # Solve\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
@@ -130,15 +142,12 @@
|
||||
"\n",
|
||||
" # Print solution.\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" print(f'Total cost = {solver.ObjectiveValue()}')\n",
|
||||
" print()\n",
|
||||
" for i in range(num_workers):\n",
|
||||
" for j in range(num_tasks):\n",
|
||||
" if solver.BooleanValue(x[i][j]):\n",
|
||||
" print(\n",
|
||||
" f'Worker {i} assigned to task {j} Cost = {costs[i][j]}')\n",
|
||||
" print(f\"Total cost = {solver.ObjectiveValue()}\\n\")\n",
|
||||
" selected = data.loc[solver.BooleanValues(x).loc[lambda x: x].index]\n",
|
||||
" for unused_index, row in selected.iterrows():\n",
|
||||
" print(f\"{row.task} assigned to {row.worker} with a cost of {row.cost}\")\n",
|
||||
" else:\n",
|
||||
" print('No solution found.')\n",
|
||||
" print(\"No solution found.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -114,14 +114,15 @@
|
||||
" x = {}\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" x[worker, task] = model.NewBoolVar(f'x[{worker},{task}]')\n",
|
||||
" x[worker, task] = model.NewBoolVar(f\"x[{worker},{task}]\")\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
" # Each worker is assigned to at most one task.\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" model.Add(\n",
|
||||
" sum(task_sizes[task] * x[worker, task]\n",
|
||||
" for task in range(num_tasks)) <= total_size_max)\n",
|
||||
" sum(task_sizes[task] * x[worker, task] for task in range(num_tasks))\n",
|
||||
" <= total_size_max\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Each task is assigned to exactly one worker.\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
@@ -140,14 +141,16 @@
|
||||
"\n",
|
||||
" # Print solution.\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" print(f'Total cost = {solver.ObjectiveValue()}\\n')\n",
|
||||
" print(f\"Total cost = {solver.ObjectiveValue()}\\n\")\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" if solver.BooleanValue(x[worker, task]):\n",
|
||||
" print(f'Worker {worker} assigned to task {task}.' +\n",
|
||||
" f' Cost = {costs[worker][task]}')\n",
|
||||
" print(\n",
|
||||
" f\"Worker {worker} assigned to task {task}.\"\n",
|
||||
" + f\" Cost = {costs[worker][task]}\"\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('No solution found.')\n",
|
||||
" print(\"No solution found.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
" x = {}\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" x[worker, task] = model.NewBoolVar(f'x[{worker},{task}]')\n",
|
||||
" x[worker, task] = model.NewBoolVar(f\"x[{worker},{task}]\")\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
" # Each worker is assigned to at most one task.\n",
|
||||
@@ -148,14 +148,16 @@
|
||||
"\n",
|
||||
" # Print solution.\n",
|
||||
" if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:\n",
|
||||
" print(f'Total cost = {solver.ObjectiveValue()}\\n')\n",
|
||||
" print(f\"Total cost = {solver.ObjectiveValue()}\\n\")\n",
|
||||
" for worker in range(num_workers):\n",
|
||||
" for task in range(num_tasks):\n",
|
||||
" if solver.BooleanValue(x[worker, task]):\n",
|
||||
" print(f'Worker {worker} assigned to task {task}.' +\n",
|
||||
" f' Cost = {costs[worker][task]}')\n",
|
||||
" print(\n",
|
||||
" f\"Worker {worker} assigned to task {task}.\"\n",
|
||||
" + f\" Cost = {costs[worker][task]}\"\n",
|
||||
" )\n",
|
||||
" else:\n",
|
||||
" print('No solution found.')\n",
|
||||
" print(\"No solution found.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"id": "description",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"Code sample that solves a model and gets the infeasibility assumptions."
|
||||
]
|
||||
},
|
||||
@@ -91,12 +92,12 @@
|
||||
" model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
" # Creates the variables.\n",
|
||||
" x = model.NewIntVar(0, 10, 'x')\n",
|
||||
" y = model.NewIntVar(0, 10, 'y')\n",
|
||||
" z = model.NewIntVar(0, 10, 'z')\n",
|
||||
" a = model.NewBoolVar('a')\n",
|
||||
" b = model.NewBoolVar('b')\n",
|
||||
" c = model.NewBoolVar('c')\n",
|
||||
" x = model.NewIntVar(0, 10, \"x\")\n",
|
||||
" y = model.NewIntVar(0, 10, \"y\")\n",
|
||||
" z = model.NewIntVar(0, 10, \"z\")\n",
|
||||
" a = model.NewBoolVar(\"a\")\n",
|
||||
" b = model.NewBoolVar(\"b\")\n",
|
||||
" c = model.NewBoolVar(\"c\")\n",
|
||||
"\n",
|
||||
" # Creates the constraints.\n",
|
||||
" model.Add(x > y).OnlyEnforceIf(a)\n",
|
||||
@@ -111,10 +112,12 @@
|
||||
" status = solver.Solve(model)\n",
|
||||
"\n",
|
||||
" # Print solution.\n",
|
||||
" print(f'Status = {solver.StatusName(status)}')\n",
|
||||
" print(f\"Status = {solver.StatusName(status)}\")\n",
|
||||
" if status == cp_model.INFEASIBLE:\n",
|
||||
" print('SufficientAssumptionsForInfeasibility = '\n",
|
||||
" f'{solver.SufficientAssumptionsForInfeasibility()}')\n",
|
||||
" print(\n",
|
||||
" \"SufficientAssumptionsForInfeasibility = \"\n",
|
||||
" f\"{solver.SufficientAssumptionsForInfeasibility()}\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
|
||||
193
examples/notebook/sat/bin_packing_sat.ipynb
Normal file
193
examples/notebook/sat/bin_packing_sat.ipynb
Normal file
@@ -0,0 +1,193 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "google",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"##### Copyright 2022 Google LLC."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "apache",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
||||
"you may not use this file except in compliance with the License.\n",
|
||||
"You may obtain a copy of the License at\n",
|
||||
"\n",
|
||||
" http://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"\n",
|
||||
"Unless required by applicable law or agreed to in writing, software\n",
|
||||
"distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"See the License for the specific language governing permissions and\n",
|
||||
"limitations under the License.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "basename",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# bin_packing_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/bin_packing_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/bin_packing_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",
|
||||
"Solve a simple bin packing problem using a MIP solver."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "code",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import io\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"from ortools.sat.python import cp_model\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_data_model():\n",
|
||||
" \"\"\"Create the data for the example.\"\"\"\n",
|
||||
"\n",
|
||||
" items_str = \"\"\"\n",
|
||||
" item weight\n",
|
||||
" i1 48\n",
|
||||
" i2 30\n",
|
||||
" i3 19\n",
|
||||
" i4 36\n",
|
||||
" i5 36\n",
|
||||
" i6 27\n",
|
||||
" i7 42\n",
|
||||
" i8 42\n",
|
||||
" i9 36\n",
|
||||
" i10 24\n",
|
||||
" i11 30\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" bins_str = \"\"\"\n",
|
||||
" bin capacity\n",
|
||||
" b1 100\n",
|
||||
" b2 100\n",
|
||||
" b3 100\n",
|
||||
" b4 100\n",
|
||||
" b5 100\n",
|
||||
" b6 100\n",
|
||||
" b7 100\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" items = pd.read_table(io.StringIO(items_str), index_col=0, sep=r\"\\s+\")\n",
|
||||
" bins = pd.read_table(io.StringIO(bins_str), index_col=0, sep=r\"\\s+\")\n",
|
||||
" return items, bins\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def main():\n",
|
||||
" items, bins = create_data_model()\n",
|
||||
"\n",
|
||||
" # Create the model.\n",
|
||||
" model = cp_model.CpModel()\n",
|
||||
"\n",
|
||||
" # Variables\n",
|
||||
" # x[i, j] = 1 if item i is packed in bin j.\n",
|
||||
" items_x_bins = pd.MultiIndex.from_product(\n",
|
||||
" [items.index, bins.index], names=[\"item\", \"bin\"]\n",
|
||||
" )\n",
|
||||
" x = model.NewBoolVarSeries(name=\"x\", index=items_x_bins)\n",
|
||||
"\n",
|
||||
" # y[j] = 1 if bin j is used.\n",
|
||||
" y = model.NewBoolVarSeries(name=\"y\", index=bins.index)\n",
|
||||
"\n",
|
||||
" # Constraints\n",
|
||||
" # Each item must be in exactly one bin.\n",
|
||||
" for unused_name, all_copies in x.groupby(\"item\"):\n",
|
||||
" model.AddExactlyOne(x[all_copies.index])\n",
|
||||
"\n",
|
||||
" # The amount packed in each bin cannot exceed its capacity.\n",
|
||||
" for selected_bin in bins.index:\n",
|
||||
" items_in_bin = x.xs(selected_bin, level=\"bin\")\n",
|
||||
" model.Add(\n",
|
||||
" items_in_bin.dot(items.weight)\n",
|
||||
" <= bins.loc[selected_bin].capacity * y[selected_bin]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Objective: minimize the number of bins used.\n",
|
||||
" model.Minimize(y.sum())\n",
|
||||
"\n",
|
||||
" # Create the solver with the CP-SAT backend, and solve the model.\n",
|
||||
" solver = cp_model.CpSolver()\n",
|
||||
" status = solver.Solve(model)\n",
|
||||
"\n",
|
||||
" if status == cp_model.OPTIMAL:\n",
|
||||
" print(f\"Number of bins used = {solver.ObjectiveValue()}\")\n",
|
||||
"\n",
|
||||
" x_values = solver.BooleanValues(x)\n",
|
||||
" y_values = solver.BooleanValues(y)\n",
|
||||
" active_bins = y_values.loc[lambda x: x].index\n",
|
||||
"\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",
|
||||
" 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",
|
||||
"\n",
|
||||
" print(f\"Total packed weight: {items.weight.sum()}\")\n",
|
||||
" print()\n",
|
||||
" print(f\"Time = {solver.WallTime()} seconds\")\n",
|
||||
" else:\n",
|
||||
" print(\"The problem does not have an optimal solution.\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"main()\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user