{ "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 }