remove std:: from std::min|max in comments; performance improvements on the SAT, bug fixes
This commit is contained in:
123
examples/python/rostering_with_travel.py
Normal file
123
examples/python/rostering_with_travel.py
Normal file
@@ -0,0 +1,123 @@
|
||||
from ortools.sat.python import cp_model
|
||||
|
||||
|
||||
def SolveRosteringWithTravel():
|
||||
model = cp_model.CpModel()
|
||||
|
||||
# [duration, start, end, location]
|
||||
jobs = [[3, 0, 6, 1],
|
||||
[5, 0, 6, 0],
|
||||
[1, 3, 7, 1],
|
||||
[1, 3, 5, 0],
|
||||
[3, 0, 3, 0],
|
||||
[3, 0, 8, 0]]
|
||||
|
||||
max_length = 20
|
||||
|
||||
num_machines = 3
|
||||
all_machines = range(num_machines)
|
||||
|
||||
horizon = 20
|
||||
travel_time = 1
|
||||
num_jobs = len(jobs)
|
||||
all_jobs = range(num_jobs)
|
||||
|
||||
intervals = []
|
||||
optional_intervals = []
|
||||
performed = []
|
||||
starts = []
|
||||
ends = []
|
||||
travels = []
|
||||
|
||||
for m in all_machines:
|
||||
optional_intervals.append([])
|
||||
|
||||
for i in all_jobs:
|
||||
# Create main interval.
|
||||
start = model.NewIntVar(jobs[i][1], horizon, 'start_%i' % i)
|
||||
duration = jobs[i][0]
|
||||
end = model.NewIntVar(0, jobs[i][2], 'end_%i' % i)
|
||||
interval = model.NewIntervalVar(start, duration, end, 'interval_%i' % i)
|
||||
starts.append(start)
|
||||
intervals.append(interval)
|
||||
ends.append(end)
|
||||
|
||||
job_performed = []
|
||||
job_travels = []
|
||||
for m in all_machines:
|
||||
performed_on_m = model.NewBoolVar('perform_%i_on_m%i' % (i, m))
|
||||
job_performed.append(performed_on_m)
|
||||
|
||||
# Create an optional copy of interval to be executed on a machine
|
||||
location0 = model.NewOptionalIntVar(
|
||||
jobs[i][3], jobs[i][3], performed_on_m, 'location_%i_on_m%i' % (i, m))
|
||||
start0 = model.NewOptionalIntVar(jobs[i][1], horizon, performed_on_m,
|
||||
'start_%i_on_m%i' % (i, m))
|
||||
end0 = model.NewOptionalIntVar(0, jobs[i][2], performed_on_m,
|
||||
'end_%i_on_m%i' % (i, m))
|
||||
interval0 = model.NewOptionalIntervalVar(
|
||||
start0, duration, end0, performed_on_m, 'interval_%i_on_m%i' % (i, m))
|
||||
optional_intervals[m].append(interval0)
|
||||
|
||||
# We only propagate the constraint if the tasks is performed on the machine.
|
||||
model.Add(start0 == start).OnlyEnforceIf(performed_on_m)
|
||||
# Adding travel constraint
|
||||
travel = model.NewBoolVar('is_travel_%i_on_m%i' % (i, m))
|
||||
startT = model.NewOptionalIntVar(0, horizon, travel,
|
||||
'start_%i_on_m%i' % (i, m))
|
||||
endT = model.NewOptionalIntVar(0, horizon, travel, 'end_%i_on_m%i' % (i,
|
||||
m))
|
||||
intervalT = model.NewOptionalIntervalVar(
|
||||
startT, travel_time, endT, travel, 'travel_interval_%i_on_m%i' % (i,
|
||||
m))
|
||||
optional_intervals[m].append(intervalT)
|
||||
job_travels.append(travel)
|
||||
|
||||
model.Add(end0 == startT).OnlyEnforceIf(travel)
|
||||
|
||||
performed.append(job_performed)
|
||||
travels.append(job_travels)
|
||||
|
||||
model.Add(sum(job_performed) == 1)
|
||||
|
||||
for m in all_machines:
|
||||
if m == 1:
|
||||
for i in all_jobs:
|
||||
if i == 2:
|
||||
for c in all_jobs:
|
||||
if (i != c) and (jobs[i][3] != jobs[c][3]):
|
||||
is_job_earlier = model.NewBoolVar('is_j%i_earlier_j%i' % (i, c))
|
||||
model.Add(starts[i] < starts[c]).OnlyEnforceIf(is_job_earlier)
|
||||
model.Add(starts[i] >= starts[c]).OnlyEnforceIf(is_job_earlier.Not())
|
||||
|
||||
# Max Length constraint (modeled as a cumulative)
|
||||
# model.AddCumulative(intervals, demands, max_length)
|
||||
|
||||
# Choose which machine to perform the jobs on.
|
||||
for m in all_machines:
|
||||
model.AddNoOverlap(optional_intervals[m])
|
||||
|
||||
# Objective variable.
|
||||
total_cost = model.NewIntVar(0, 1000, 'cost')
|
||||
model.Add(total_cost == sum(
|
||||
performed[j][m] * (10 * (m + 1)) for j in all_jobs for m in all_machines))
|
||||
model.Minimize(total_cost)
|
||||
|
||||
# Solve model.
|
||||
solver = cp_model.CpSolver()
|
||||
result = solver.Solve(model)
|
||||
|
||||
print()
|
||||
print(result)
|
||||
print('Statistics')
|
||||
print(' - conflicts : %i' % solver.NumConflicts())
|
||||
print(' - branches : %i' % solver.NumBranches())
|
||||
print(' - wall time : %f ms' % solver.WallTime())
|
||||
|
||||
|
||||
def main():
|
||||
SolveRosteringWithTravel()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
176
examples/python/school_scheduling_sat.py
Normal file
176
examples/python/school_scheduling_sat.py
Normal file
@@ -0,0 +1,176 @@
|
||||
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)
|
||||
|
||||
# Solution collector
|
||||
self.collector = None
|
||||
|
||||
def solve(self):
|
||||
print('Solving')
|
||||
solver = cp_model.CpSolver()
|
||||
solution_printer = SchoolSchedulingSatSolutionPrinter()
|
||||
status = solver.SearchForAllSolutions(self.model, solution_printer)
|
||||
print()
|
||||
print('Branches', solver.NumBranches())
|
||||
print('Conflicts', solver.NumConflicts())
|
||||
print('WallTime', solver.WallTime())
|
||||
|
||||
def print_status(self):
|
||||
pass
|
||||
|
||||
|
||||
class SchoolSchedulingSatSolutionPrinter(cp_model.CpSolverSolutionCallback):
|
||||
|
||||
def NewSolution(self):
|
||||
print('Found Solution!')
|
||||
|
||||
|
||||
def main():
|
||||
# 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()
|
||||
solver.print_status()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user