474 lines
18 KiB
Plaintext
474 lines
18 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Copyright 2010-2017 Google\n",
|
|
"# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
|
"# you may not use this file except in compliance with the License.\n",
|
|
"# You may obtain a copy of the License at\n",
|
|
"#\n",
|
|
"# http://www.apache.org/licenses/LICENSE-2.0\n",
|
|
"#\n",
|
|
"# Unless required by applicable law or agreed to in writing, software\n",
|
|
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
|
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
|
"# See the License for the specific language governing permissions and\n",
|
|
"# limitations under the License.\n",
|
|
"\"\"\"SAT code samples used in documentation.\"\"\"\n",
|
|
"\n",
|
|
"from __future__ import absolute_import\n",
|
|
"from __future__ import division\n",
|
|
"from __future__ import print_function\n",
|
|
"\n",
|
|
"import collections\n",
|
|
"\n",
|
|
"from ortools.sat.python import cp_model\n",
|
|
"\n",
|
|
"\n",
|
|
"def CodeSample():\n",
|
|
" model = cp_model.CpModel()\n",
|
|
" x = model.NewBoolVar('x')\n",
|
|
" print(x)\n",
|
|
"\n",
|
|
"\n",
|
|
"def LiteralSample():\n",
|
|
" model = cp_model.CpModel()\n",
|
|
" x = model.NewBoolVar('x')\n",
|
|
" not_x = x.Not()\n",
|
|
" print(x)\n",
|
|
" print(not_x)\n",
|
|
"\n",
|
|
"\n",
|
|
"def BoolOrSample():\n",
|
|
" model = cp_model.CpModel()\n",
|
|
"\n",
|
|
" x = model.NewBoolVar('x')\n",
|
|
" y = model.NewBoolVar('y')\n",
|
|
"\n",
|
|
" model.AddBoolOr([x, y.Not()])\n",
|
|
"\n",
|
|
"\n",
|
|
"def ReifiedSample():\n",
|
|
" \"\"\"Showcase creating a reified constraint.\"\"\"\n",
|
|
" model = cp_model.CpModel()\n",
|
|
"\n",
|
|
" x = model.NewBoolVar('x')\n",
|
|
" y = model.NewBoolVar('y')\n",
|
|
" b = model.NewBoolVar('b')\n",
|
|
"\n",
|
|
" # First version using a half-reified bool and.\n",
|
|
" model.AddBoolAnd([x, y.Not()]).OnlyEnforceIf(b)\n",
|
|
"\n",
|
|
" # Second version using implications.\n",
|
|
" model.AddImplication(b, x)\n",
|
|
" model.AddImplication(b, y.Not())\n",
|
|
"\n",
|
|
" # Third version using bool or.\n",
|
|
" model.AddBoolOr([b.Not(), x])\n",
|
|
" model.AddBoolOr([b.Not(), y.Not()])\n",
|
|
"\n",
|
|
"\n",
|
|
"def RabbitsAndPheasants():\n",
|
|
" \"\"\"Solves the rabbits + pheasants problem.\"\"\"\n",
|
|
" model = cp_model.CpModel()\n",
|
|
"\n",
|
|
" r = model.NewIntVar(0, 100, 'r')\n",
|
|
" p = model.NewIntVar(0, 100, 'p')\n",
|
|
"\n",
|
|
" # 20 heads.\n",
|
|
" model.Add(r + p == 20)\n",
|
|
" # 56 legs.\n",
|
|
" model.Add(4 * r + 2 * p == 56)\n",
|
|
"\n",
|
|
" # Solves and prints out the solution.\n",
|
|
" solver = cp_model.CpSolver()\n",
|
|
" status = solver.Solve(model)\n",
|
|
"\n",
|
|
" if status == cp_model.FEASIBLE:\n",
|
|
" print('%i rabbits and %i pheasants' % (solver.Value(r), solver.Value(p)))\n",
|
|
"\n",
|
|
"\n",
|
|
"def BinpackingProblem():\n",
|
|
" \"\"\"Solves a bin-packing problem.\"\"\"\n",
|
|
" # Data.\n",
|
|
" bin_capacity = 100\n",
|
|
" slack_capacity = 20\n",
|
|
" num_bins = 10\n",
|
|
" all_bins = range(num_bins)\n",
|
|
"\n",
|
|
" items = [(20, 12), (15, 12), (30, 8), (45, 5)]\n",
|
|
" num_items = len(items)\n",
|
|
" all_items = range(num_items)\n",
|
|
"\n",
|
|
" # Model.\n",
|
|
" model = cp_model.CpModel()\n",
|
|
"\n",
|
|
" # Main variables.\n",
|
|
" x = {}\n",
|
|
" for i in all_items:\n",
|
|
" num_copies = items[i][1]\n",
|
|
" for b in all_bins:\n",
|
|
" x[(i, b)] = model.NewIntVar(0, num_copies, 'x_%i_%i' % (i, b))\n",
|
|
"\n",
|
|
" # Load variables.\n",
|
|
" load = [model.NewIntVar(0, bin_capacity, 'load_%i' % b) for b in all_bins]\n",
|
|
"\n",
|
|
" # Slack variables.\n",
|
|
" slacks = [model.NewBoolVar('slack_%i' % b) for b in all_bins]\n",
|
|
"\n",
|
|
" # Links load and x.\n",
|
|
" for b in all_bins:\n",
|
|
" model.Add(load[b] == sum(x[(i, b)] * items[i][0] for i in all_items))\n",
|
|
"\n",
|
|
" # Place all items.\n",
|
|
" for i in all_items:\n",
|
|
" model.Add(sum(x[(i, b)] for b in all_bins) == items[i][1])\n",
|
|
"\n",
|
|
" # Links load and slack through an equivalence relation.\n",
|
|
" safe_capacity = bin_capacity - slack_capacity\n",
|
|
" for b in all_bins:\n",
|
|
" # slack[b] => load[b] <= safe_capacity.\n",
|
|
" model.Add(load[b] <= safe_capacity).OnlyEnforceIf(slacks[b])\n",
|
|
" # not(slack[b]) => load[b] > safe_capacity.\n",
|
|
" model.Add(load[b] > safe_capacity).OnlyEnforceIf(slacks[b].Not())\n",
|
|
"\n",
|
|
" # Maximize sum of slacks.\n",
|
|
" model.Maximize(sum(slacks))\n",
|
|
"\n",
|
|
" # Solves and prints out the solution.\n",
|
|
" solver = cp_model.CpSolver()\n",
|
|
" status = solver.Solve(model)\n",
|
|
" print('Solve status: %s' % solver.StatusName(status))\n",
|
|
" if status == cp_model.OPTIMAL:\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",
|
|
"def IntervalSample():\n",
|
|
" model = cp_model.CpModel()\n",
|
|
" horizon = 100\n",
|
|
" start_var = model.NewIntVar(0, horizon, 'start')\n",
|
|
" duration = 10 # Python cp/sat code accept integer variables or constants.\n",
|
|
" end_var = model.NewIntVar(0, horizon, 'end')\n",
|
|
" interval_var = model.NewIntervalVar(start_var, duration, end_var, 'interval')\n",
|
|
" print('start = %s, duration = %i, end = %s, interval = %s' %\n",
|
|
" (start_var, duration, end_var, interval_var))\n",
|
|
"\n",
|
|
"\n",
|
|
"def OptionalIntervalSample():\n",
|
|
" model = cp_model.CpModel()\n",
|
|
" horizon = 100\n",
|
|
" start_var = model.NewIntVar(0, horizon, 'start')\n",
|
|
" duration = 10 # Python cp/sat code accept integer variables or constants.\n",
|
|
" end_var = model.NewIntVar(0, horizon, 'end')\n",
|
|
" presence_var = model.NewBoolVar('presence')\n",
|
|
" interval_var = model.NewOptionalIntervalVar(start_var, duration, end_var,\n",
|
|
" presence_var, 'interval')\n",
|
|
" print('start = %s, duration = %i, end = %s, presence = %s, interval = %s' %\n",
|
|
" (start_var, duration, end_var, presence_var, interval_var))\n",
|
|
"\n",
|
|
"\n",
|
|
"def MinimalCpSat():\n",
|
|
" \"\"\"Minimal CP-SAT example to showcase calling the solver.\"\"\"\n",
|
|
" # Creates the model.\n",
|
|
" model = cp_model.CpModel()\n",
|
|
" # Creates the variables.\n",
|
|
" num_vals = 3\n",
|
|
" x = model.NewIntVar(0, num_vals - 1, 'x')\n",
|
|
" y = model.NewIntVar(0, num_vals - 1, 'y')\n",
|
|
" z = model.NewIntVar(0, num_vals - 1, 'z')\n",
|
|
" # Creates the constraints.\n",
|
|
" model.Add(x != y)\n",
|
|
"\n",
|
|
" # Creates a solver and solves the model.\n",
|
|
" solver = cp_model.CpSolver()\n",
|
|
" status = solver.Solve(model)\n",
|
|
"\n",
|
|
" if status == cp_model.FEASIBLE:\n",
|
|
" print('x = %i' % solver.Value(x))\n",
|
|
" print('y = %i' % solver.Value(y))\n",
|
|
" print('z = %i' % solver.Value(z))\n",
|
|
"\n",
|
|
"\n",
|
|
"def MinimalCpSatWithTimeLimit():\n",
|
|
" \"\"\"Minimal CP-SAT example to showcase calling the solver.\"\"\"\n",
|
|
" # Creates the model.\n",
|
|
" model = cp_model.CpModel()\n",
|
|
" # Creates the variables.\n",
|
|
" num_vals = 3\n",
|
|
" x = model.NewIntVar(0, num_vals - 1, 'x')\n",
|
|
" y = model.NewIntVar(0, num_vals - 1, 'y')\n",
|
|
" z = model.NewIntVar(0, num_vals - 1, 'z')\n",
|
|
" # Adds an all-different constraint.\n",
|
|
" model.Add(x != y)\n",
|
|
"\n",
|
|
" # Creates a solver and solves the model.\n",
|
|
" solver = cp_model.CpSolver()\n",
|
|
"\n",
|
|
" # Sets a time limit of 10 seconds.\n",
|
|
" solver.parameters.max_time_in_seconds = 10.0\n",
|
|
"\n",
|
|
" status = solver.Solve(model)\n",
|
|
"\n",
|
|
" if status == cp_model.FEASIBLE:\n",
|
|
" print('x = %i' % solver.Value(x))\n",
|
|
" print('y = %i' % solver.Value(y))\n",
|
|
" print('z = %i' % solver.Value(z))\n",
|
|
"\n",
|
|
"\n",
|
|
"# You need to subclass the cp_model.CpSolverSolutionCallback class.\n",
|
|
"class VarArrayAndObjectiveSolutionPrinter(cp_model.CpSolverSolutionCallback):\n",
|
|
" \"\"\"Print intermediate solutions.\"\"\"\n",
|
|
"\n",
|
|
" def __init__(self, variables):\n",
|
|
" cp_model.CpSolverSolutionCallback.__init__(self)\n",
|
|
" self.__variables = variables\n",
|
|
" self.__solution_count = 0\n",
|
|
"\n",
|
|
" def OnSolutionCallback(self):\n",
|
|
" print('Solution %i' % self.__solution_count)\n",
|
|
" print(' objective value = %i' % self.ObjectiveValue())\n",
|
|
" for v in self.__variables:\n",
|
|
" print(' %s = %i' % (v, self.Value(v)), end=' ')\n",
|
|
" print()\n",
|
|
" self.__solution_count += 1\n",
|
|
"\n",
|
|
" def SolutionCount(self):\n",
|
|
" return self.__solution_count\n",
|
|
"\n",
|
|
"\n",
|
|
"def MinimalCpSatPrintIntermediateSolutions():\n",
|
|
" \"\"\"Showcases printing intermediate solutions found during search.\"\"\"\n",
|
|
" # Creates the model.\n",
|
|
" model = cp_model.CpModel()\n",
|
|
" # Creates the variables.\n",
|
|
" num_vals = 3\n",
|
|
" x = model.NewIntVar(0, num_vals - 1, 'x')\n",
|
|
" y = model.NewIntVar(0, num_vals - 1, 'y')\n",
|
|
" z = model.NewIntVar(0, num_vals - 1, 'z')\n",
|
|
" # Creates the constraints.\n",
|
|
" model.Add(x != y)\n",
|
|
" model.Maximize(x + 2 * y + 3 * z)\n",
|
|
"\n",
|
|
" # Creates a solver and solves.\n",
|
|
" solver = cp_model.CpSolver()\n",
|
|
" solution_printer = VarArrayAndObjectiveSolutionPrinter([x, y, z])\n",
|
|
" status = solver.SolveWithSolutionCallback(model, solution_printer)\n",
|
|
"\n",
|
|
" print('Status = %s' % solver.StatusName(status))\n",
|
|
" print('Number of solutions found: %i' % solution_printer.SolutionCount())\n",
|
|
"\n",
|
|
"\n",
|
|
"class VarArraySolutionPrinter(cp_model.CpSolverSolutionCallback):\n",
|
|
" \"\"\"Print intermediate solutions.\"\"\"\n",
|
|
"\n",
|
|
" def __init__(self, variables):\n",
|
|
" cp_model.CpSolverSolutionCallback.__init__(self)\n",
|
|
" self.__variables = variables\n",
|
|
" self.__solution_count = 0\n",
|
|
"\n",
|
|
" def OnSolutionCallback(self):\n",
|
|
" self.__solution_count += 1\n",
|
|
" for v in self.__variables:\n",
|
|
" print('%s=%i' % (v, self.Value(v)), end=' ')\n",
|
|
" print()\n",
|
|
"\n",
|
|
" def SolutionCount(self):\n",
|
|
" return self.__solution_count\n",
|
|
"\n",
|
|
"\n",
|
|
"def MinimalCpSatAllSolutions():\n",
|
|
" \"\"\"Showcases calling the solver to search for all solutions.\"\"\"\n",
|
|
" # Creates the model.\n",
|
|
" model = cp_model.CpModel()\n",
|
|
" # Creates the variables.\n",
|
|
" num_vals = 3\n",
|
|
" x = model.NewIntVar(0, num_vals - 1, 'x')\n",
|
|
" y = model.NewIntVar(0, num_vals - 1, 'y')\n",
|
|
" z = model.NewIntVar(0, num_vals - 1, 'z')\n",
|
|
" # Create the constraints.\n",
|
|
" model.Add(x != y)\n",
|
|
"\n",
|
|
" # Create a solver and solve.\n",
|
|
" solver = cp_model.CpSolver()\n",
|
|
" solution_printer = VarArraySolutionPrinter([x, y, z])\n",
|
|
" status = solver.SearchForAllSolutions(model, solution_printer)\n",
|
|
" print('Status = %s' % solver.StatusName(status))\n",
|
|
" print('Number of solutions found: %i' % solution_printer.SolutionCount())\n",
|
|
"\n",
|
|
"\n",
|
|
"def SolvingLinearProblem():\n",
|
|
" \"\"\"CP-SAT linear solver problem.\"\"\"\n",
|
|
" # Create a model.\n",
|
|
" model = cp_model.CpModel()\n",
|
|
"\n",
|
|
" # x and y are integer non-negative variables.\n",
|
|
" x = model.NewIntVar(0, 17, 'x')\n",
|
|
" y = model.NewIntVar(0, 17, 'y')\n",
|
|
" model.Add(2 * x + 14 * y <= 35)\n",
|
|
" model.Add(2 * x <= 7)\n",
|
|
" obj_var = model.NewIntVar(0, 1000, 'obj_var')\n",
|
|
" model.Add(obj_var == x + 10 * y)\n",
|
|
" model.Maximize(obj_var)\n",
|
|
"\n",
|
|
" # Create a solver and solve.\n",
|
|
" solver = cp_model.CpSolver()\n",
|
|
" status = solver.Solve(model)\n",
|
|
" if status == cp_model.OPTIMAL:\n",
|
|
" print('Objective value: %i' % solver.ObjectiveValue())\n",
|
|
" print()\n",
|
|
" print('x= %i' % solver.Value(x))\n",
|
|
" print('y= %i' % solver.Value(y))\n",
|
|
"\n",
|
|
"\n",
|
|
"def MinimalJobShop():\n",
|
|
" \"\"\"Minimal jobshop problem.\"\"\"\n",
|
|
" # Create the model.\n",
|
|
" model = cp_model.CpModel()\n",
|
|
"\n",
|
|
" machines_count = 3\n",
|
|
" jobs_count = 3\n",
|
|
" all_machines = range(0, machines_count)\n",
|
|
" all_jobs = range(0, jobs_count)\n",
|
|
" # Define data.\n",
|
|
" machines = [[0, 1, 2], [0, 2, 1], [1, 2]]\n",
|
|
"\n",
|
|
" processing_times = [[3, 2, 2], [2, 1, 4], [4, 3]]\n",
|
|
" # Computes horizon.\n",
|
|
" horizon = 0\n",
|
|
" for job in all_jobs:\n",
|
|
" horizon += sum(processing_times[job])\n",
|
|
"\n",
|
|
" task_type = collections.namedtuple('task_type', 'start end interval')\n",
|
|
" assigned_task_type = collections.namedtuple('assigned_task_type',\n",
|
|
" 'start job index')\n",
|
|
"\n",
|
|
" # Creates jobs.\n",
|
|
" all_tasks = {}\n",
|
|
" for job in all_jobs:\n",
|
|
" for index in range(0, len(machines[job])):\n",
|
|
" start_var = model.NewIntVar(0, horizon, 'start_%i_%i' % (job, index))\n",
|
|
" duration = processing_times[job][index]\n",
|
|
" end_var = model.NewIntVar(0, horizon, 'end_%i_%i' % (job, index))\n",
|
|
" interval_var = model.NewIntervalVar(start_var, duration, end_var,\n",
|
|
" 'interval_%i_%i' % (job, index))\n",
|
|
" all_tasks[(job, index)] = task_type(\n",
|
|
" start=start_var, end=end_var, interval=interval_var)\n",
|
|
"\n",
|
|
" # Creates sequence variables and add disjunctive constraints.\n",
|
|
" for machine in all_machines:\n",
|
|
" intervals = []\n",
|
|
" for job in all_jobs:\n",
|
|
" for index in range(0, len(machines[job])):\n",
|
|
" if machines[job][index] == machine:\n",
|
|
" intervals.append(all_tasks[(job, index)].interval)\n",
|
|
" model.AddNoOverlap(intervals)\n",
|
|
"\n",
|
|
" # Add precedence contraints.\n",
|
|
" for job in all_jobs:\n",
|
|
" for index in range(0, len(machines[job]) - 1):\n",
|
|
" model.Add(all_tasks[(job, index + 1)].start >= all_tasks[(job,\n",
|
|
" index)].end)\n",
|
|
"\n",
|
|
" # Makespan objective.\n",
|
|
" obj_var = model.NewIntVar(0, horizon, 'makespan')\n",
|
|
" model.AddMaxEquality(\n",
|
|
" obj_var,\n",
|
|
" [all_tasks[(job, len(machines[job]) - 1)].end for job in all_jobs])\n",
|
|
" model.Minimize(obj_var)\n",
|
|
"\n",
|
|
" # Solve model.\n",
|
|
" solver = cp_model.CpSolver()\n",
|
|
" status = solver.Solve(model)\n",
|
|
"\n",
|
|
" if status == cp_model.OPTIMAL:\n",
|
|
" # Print out makespan.\n",
|
|
" print('Optimal Schedule Length: %i' % solver.ObjectiveValue())\n",
|
|
" print()\n",
|
|
"\n",
|
|
" # Create one list of assigned tasks per machine.\n",
|
|
" assigned_jobs = [[] for _ in range(machines_count)]\n",
|
|
" for job in all_jobs:\n",
|
|
" for index in range(len(machines[job])):\n",
|
|
" machine = machines[job][index]\n",
|
|
" assigned_jobs[machine].append(\n",
|
|
" assigned_task_type(\n",
|
|
" start=solver.Value(all_tasks[(job, index)].start),\n",
|
|
" job=job,\n",
|
|
" index=index))\n",
|
|
"\n",
|
|
" disp_col_width = 10\n",
|
|
" sol_line = ''\n",
|
|
" sol_line_tasks = ''\n",
|
|
"\n",
|
|
" print('Optimal Schedule', '\\n')\n",
|
|
"\n",
|
|
" for machine in all_machines:\n",
|
|
" # Sort by starting time.\n",
|
|
" assigned_jobs[machine].sort()\n",
|
|
" sol_line += 'Machine ' + str(machine) + ': '\n",
|
|
" sol_line_tasks += 'Machine ' + str(machine) + ': '\n",
|
|
"\n",
|
|
" for assigned_task in assigned_jobs[machine]:\n",
|
|
" name = 'job_%i_%i' % (assigned_task.job, assigned_task.index)\n",
|
|
" # Add spaces to output to align columns.\n",
|
|
" sol_line_tasks += name + ' ' * (disp_col_width - len(name))\n",
|
|
" start = assigned_task.start\n",
|
|
" duration = processing_times[assigned_task.job][assigned_task.index]\n",
|
|
"\n",
|
|
" sol_tmp = '[%i,%i]' % (start, start + duration)\n",
|
|
" # Add spaces to output to align columns.\n",
|
|
" sol_line += sol_tmp + ' ' * (disp_col_width - len(sol_tmp))\n",
|
|
"\n",
|
|
" sol_line += '\\n'\n",
|
|
" sol_line_tasks += '\\n'\n",
|
|
"\n",
|
|
" print(sol_line_tasks)\n",
|
|
" print('Time Intervals for task_types\\n')\n",
|
|
" print(sol_line)\n",
|
|
"\n",
|
|
"\n",
|
|
"print('--- CodeSample ---')\n",
|
|
"CodeSample()\n",
|
|
"print('--- LiteralSample ---')\n",
|
|
"LiteralSample()\n",
|
|
"print('--- BoolOrSample ---')\n",
|
|
"BoolOrSample()\n",
|
|
"print('--- ReifiedSample ---')\n",
|
|
"ReifiedSample()\n",
|
|
"print('--- RabbitsAndPheasants ---')\n",
|
|
"RabbitsAndPheasants()\n",
|
|
"print('--- BinpackingProblem ---')\n",
|
|
"BinpackingProblem()\n",
|
|
"print('--- IntervalSample ---')\n",
|
|
"IntervalSample()\n",
|
|
"print('--- OptionalIntervalSample ---')\n",
|
|
"OptionalIntervalSample()\n",
|
|
"print('--- MinimalCpSat ---')\n",
|
|
"MinimalCpSat()\n",
|
|
"print('--- MinimalCpSatWithTimeLimit ---')\n",
|
|
"MinimalCpSatWithTimeLimit()\n",
|
|
"print('--- MinimalCpSatPrintIntermediateSolutions ---')\n",
|
|
"MinimalCpSatPrintIntermediateSolutions()\n",
|
|
"print('--- MinimalCpSatAllSolutions ---')\n",
|
|
"MinimalCpSatAllSolutions()\n",
|
|
"print('--- SolvingLinearProblem ---')\n",
|
|
"SolvingLinearProblem()\n",
|
|
"print('--- MinimalJobShop ---')\n",
|
|
"MinimalJobShop()\n",
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|