pip3 install ortools
In [4]:
from ortools.linear_solver import pywraplp
In [5]:
solver = pywraplp.Solver('SolveSimpleSystem',
pywraplp.Solver.GLOP_LINEAR_PROGRAMMING)
In [3]:
type(solver)
Out[3]:
In [4]:
dir(solver)
Out[4]:
solver.NumVar(Lower Bound, Upper Bound, 변수 이름)
In [5]:
x = solver.NumVar(0, 1, 'x')
y = solver.NumVar(0, 2, 'y')
In [6]:
type(y)
Out[6]:
In [7]:
x
Out[7]:
In [8]:
dir(x)
Out[8]:
In [9]:
objective = solver.Objective()
In [10]:
dir(objective)
Out[10]:
In [11]:
objective.SetCoefficient(x, 1)
objective.SetCoefficient(y, 1)
SetMaximization() : 최대화
In [12]:
objective.SetMaximization()
In [13]:
solver.Solve()
print('Solution:')
print('x = ', x.solution_value())
print('y = ', y.solution_value())
In [19]:
type(solver)
Out[19]:
In [20]:
x
Out[20]:
In [21]:
x.solution_value()
Out[21]:
In [6]:
x = solver.NumVar(-solver.infinity(), solver.infinity(), 'x')
In [7]:
y = solver.NumVar(-solver.infinity(), solver.infinity(), 'y')
In [9]:
# Constraint 1: x + 2y <= 14
contraint1 = solver.Constraint(-solver.infinity(), 14)
contraint1.SetCoefficient(x, 1)
contraint1.SetCoefficient(y, 2)
# Constraint 2: 3x - y >= 0
contraint2 = solver.Constraint(0, solver.infinity())
contraint2.SetCoefficient(x, 3)
contraint2.SetCoefficient(y, -1)
# Constraint 3: x - y <= 2
contraint3 = solver.Constraint(-solver.infinity(), 2)
contraint3.SetCoefficient(x, 1)
contraint3.SetCoefficient(y, -1)
In [10]:
objective = solver.Objective()
In [13]:
objective.SetCoefficient(x, 3)
objective.SetCoefficient(y, 4)
objective.SetMaximization()
In [14]:
solver.Solve()
Out[14]:
In [15]:
opt_solution = 3 * x.solution_value() + 4 * y.solution_value()
In [27]:
print('Number of variables =', solver.NumVariables())
print('Number of constraints =', solver.NumConstraints())
# The value of each variable in the solution.
print('Solution:')
print('x = ', round(x.solution_value())) # 부동소수점 처리하기 위해 round
print('y = ', round(y.solution_value()))
# The objective value of the solution.
print('Optimal objective value =', round(opt_solution))
In [29]:
from ortools.sat.python import cp_model
class SchoolSchedulingProblem(object):
def __init__(self, subjects, teachers, curriculum, specialties,
working_days, periods, levels, sections, teacher_work_hours):
self.subjects = subjects
self.teachers = teachers
self.curriculum = curriculum
self.specialties = specialties
self.working_days = working_days
self.periods = periods
self.levels = levels
self.sections = sections
self.teacher_work_hours = teacher_work_hours
class SchoolSchedulingSatSolver(object):
def __init__(self, problem):
# Problem
self.problem = problem
# Utilities
self.timeslots = [
'{0:10} {1:6}'.format(x, y) for x in problem.working_days
for y in problem.periods
]
self.num_days = len(problem.working_days)
self.num_periods = len(problem.periods)
self.num_slots = len(self.timeslots)
self.num_teachers = len(problem.teachers)
self.num_subjects = len(problem.subjects)
self.num_levels = len(problem.levels)
self.num_sections = len(problem.sections)
self.courses = [
x * self.num_levels + y for x in problem.levels
for y in problem.sections
]
self.num_courses = self.num_levels * self.num_sections
all_courses = range(self.num_courses)
all_teachers = range(self.num_teachers)
all_slots = range(self.num_slots)
all_sections = range(self.num_sections)
all_subjects = range(self.num_subjects)
all_levels = range(self.num_levels)
self.model = cp_model.CpModel()
self.assignment = {}
for c in all_courses:
for s in all_subjects:
for t in all_teachers:
for slot in all_slots:
if t in self.problem.specialties[s]:
name = 'C:{%i} S:{%i} T:{%i} Slot:{%i}' % (c, s, t,
slot)
self.assignment[c, s, t,
slot] = self.model.NewBoolVar(name)
else:
name = 'NO DISP C:{%i} S:{%i} T:{%i} Slot:{%i}' % (
c, s, t, slot)
self.assignment[c, s, t,
slot] = self.model.NewIntVar(
0, 0, name)
# Constraints
# Each course must have the quantity of classes specified in the curriculum
for level in all_levels:
for section in all_sections:
course = level * self.num_sections + section
for subject in all_subjects:
required_slots = self.problem.curriculum[
self.problem.levels[level], self.problem.subjects[
subject]]
self.model.Add(
sum(self.assignment[course, subject, teacher, slot]
for slot in all_slots
for teacher in all_teachers) == required_slots)
# Teacher can do at most one class at a time
for teacher in all_teachers:
for slot in all_slots:
self.model.Add(
sum([
self.assignment[c, s, teacher, slot]
for c in all_courses for s in all_subjects
]) <= 1)
# Maximum work hours for each teacher
for teacher in all_teachers:
self.model.Add(
sum([
self.assignment[c, s, teacher, slot] for c in all_courses
for s in all_subjects for slot in all_slots
]) <= self.problem.teacher_work_hours[teacher])
# Teacher makes all the classes of a subject's course
teacher_courses = {}
for level in all_levels:
for section in all_sections:
course = level * self.num_sections + section
for subject in all_subjects:
for t in all_teachers:
name = 'C:{%i} S:{%i} T:{%i}' % (course, subject,
teacher)
teacher_courses[course, subject,
t] = self.model.NewBoolVar(name)
temp_array = [
self.assignment[course, subject, t, slot]
for slot in all_slots
]
self.model.AddMaxEquality(
teacher_courses[course, subject, t], temp_array)
self.model.Add(
sum(teacher_courses[course, subject, t]
for t in all_teachers) == 1)
def solve(self):
print('Solving')
solver = cp_model.CpSolver()
solution_printer = SchoolSchedulingSatSolutionPrinter()
status = solver.Solve(self.model)
print()
print('status', status)
print('Branches', solver.NumBranches())
print('Conflicts', solver.NumConflicts())
print('WallTime', solver.WallTime())
class SchoolSchedulingSatSolutionPrinter(cp_model.CpSolverSolutionCallback):
def __init__(self):
cp_model.CpSolverSolutionCallback.__init__(self)
self.__solution_count = 0
def OnSolutionCallback(self):
print('Found Solution!')
# DATA
subjects = ['English', 'Math', 'History']
levels = ['1-', '2-', '3-']
sections = ['A']
teachers = ['Mario', 'Elvis', 'Donald', 'Ian']
teachers_work_hours = [18, 12, 12, 18]
working_days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
periods = ['08:00-09:30', '09:45-11:15', '11:30-13:00']
curriculum = {
('1-', 'English'): 3,
('1-', 'Math'): 3,
('1-', 'History'): 2,
('2-', 'English'): 4,
('2-', 'Math'): 2,
('2-', 'History'): 2,
('3-', 'English'): 2,
('3-', 'Math'): 4,
('3-', 'History'): 2
}
# Subject -> List of teachers who can teach it
specialties_idx_inverse = [
[1, 3], # English -> Elvis & Ian
[0, 3], # Math -> Mario & Ian
[2, 3] # History -> Donald & Ian
]
problem = SchoolSchedulingProblem(
subjects, teachers, curriculum, specialties_idx_inverse, working_days,
periods, levels, sections, teachers_work_hours)
solver = SchoolSchedulingSatSolver(problem)
solver.solve()
In [37]:
from ortools.sat.python import cp_model
def schedule():
# Input data.
positions = [
1, 2, 8, 10, 5, 3, 4, 3, 6, 6, 4, 5, 4, 3, 4, 4, 3, 4, 2, 1, 0, 0, 0,
0, 1, 2, 9, 9, 4, 3, 4, 3, 5, 4, 5, 2, 5, 6, 6, 7, 4, 2, 1, 0, 0, 0, 0,
0, 0, 2, 7, 6, 5, 2, 4, 4, 6, 6, 4, 5, 5, 5, 7, 5, 4, 4, 2, 3, 1, 0, 0,
0, 1, 2, 9, 7, 2, 2, 4, 2, 4, 5, 3, 2, 6, 7, 5, 6, 4, 4, 2, 1, 0, 0, 0,
0, 2, 2, 8, 8, 6, 3, 3, 3, 10, 9, 6, 3, 3, 4, 5, 4, 5, 4, 2, 1, 0, 0,
0, 0, 1, 2, 9, 5, 5, 4, 5, 2, 5, 7, 5, 3, 4, 8, 4, 4, 2, 3, 1, 0, 0, 0,
0, 0, 1, 2, 10, 5, 5, 4, 5, 2, 4, 6, 7, 4, 4, 5, 4, 4, 3, 3, 2, 1, 0,
0, 0, 0
]
possible_shifts = [[
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 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, 0, 0, 0, 0, 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, 0, 0, 0, 0, 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, 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, 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,
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, 40
], [
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, 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, 0, 0, 0, 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, 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, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 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, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
1, 40
], [
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, 0, 0, 0, 0, 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, 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, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 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, 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, 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, 0, 0,
2, 40
], [
0, 0, 0, 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, 0, 0, 0, 0, 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, 0, 0, 0, 0, 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, 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, 0, 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, 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, 0, 0, 0, 0, 0, 0,
3, 40
], [
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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 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,
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, 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, 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, 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, 1, 1, 1, 1, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5, 16
], [
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, 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, 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, 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, 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, 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, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
6, 16
], [
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, 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, 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, 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, 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, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
7, 16
], [
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, 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, 1, 1, 1, 1, 1, 1, 0, 0, 0, 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, 0, 0, 0, 0, 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, 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, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
8, 40
]]
# Useful numbers.
num_slots = len(positions)
all_slots = range(num_slots)
num_shifts = len(possible_shifts)
all_shifts = range(num_shifts)
min_number_of_workers = [5 * x for x in positions]
num_workers = 300
# Model the problem.
model = cp_model.CpModel()
workers_per_shift = [
model.NewIntVar(0, num_workers, 'shift[%i]' % i) for i in all_shifts
]
# Satisfy min requirements.
for slot in all_slots:
model.Add(
sum(workers_per_shift[shift] * possible_shifts[shift][slot]
for shift in all_shifts) >= min_number_of_workers[slot])
# Create the objective variable.
objective = model.NewIntVar(0, num_workers, 'objective')
# Link the objective.
model.Add(sum(workers_per_shift) == objective)
# Minimize.
model.Minimize(objective)
solver = cp_model.CpSolver()
status = solver.Solve(model)
if status == cp_model.OPTIMAL:
print('Objective value = %i' % solver.ObjectiveValue())
print('Statistics')
print(' - conflicts : %i' % solver.NumConflicts())
print(' - branches : %i' % solver.NumBranches())
print(' - wall time : %f ms' % solver.WallTime())
In [38]:
schedule()
In [ ]: