Reputation: 121
Google's OR-Tools provides some example code demonstrating how to solve the nurse-scheduling problem. I'm trying to adapt it to solve an interview-planning scenario wherein a single candidate will attend 2 meetings. Each meeting has the following requirement:
Solving Requirement (1) is pretty simple:
meetings = ["phone_screen", "in_person"]
users = ["alice", "bob", "carl", "donna"]
days = ["Mon", "Tue", "Wed"]
times = ["morning", "afternoon"]
model = cp_model.CpModel()
# Build a boolean variable for every possible meeting time attendee
data = {}
for meeting in meetings:
for day in days:
for time in times:
for user in users:
id = 'meeting={},day={},time={},user={}'.format(meeting, day, time, user)
data[(meeting, day, time, user)] = model.NewBoolVar(id)
## Requirement 1: Ensure 2 attendees for each time slot
for meeting in meetings:
for day in days:
for time in times:
per_time_data = []
for user in users:
per_time_data.append(data[(meeting, day, time, user)])
model.Add(sum(per_time_data) == 2)
# Solve and print solutions
solver = cp_model.CpSolver()
solver.Solve(model)
for day in days:
for time in times:
for meeting in meetings:
string = '{} {}\t| {}\t| '.format(day, time, meeting)
for user in users:
if solver.Value(data[(meeting, day, time, user)]) == 1:
string += user + '\t'
print(string)
This works as expected, printing out a single solution wherein each meeting slot has a (basically) random selection of 2 attendees:
Mon morning | phone_screen | bob carl
Mon morning | in_person | alice bob
Mon afternoon | phone_screen | alice bob
Mon afternoon | in_person | alice bob
Tue morning | phone_screen | alice bob
Tue morning | in_person | alice bob
Tue afternoon | phone_screen | alice bob
Tue afternoon | in_person | alice bob
Wed morning | phone_screen | alice bob
Wed morning | in_person | alice bob
Wed afternoon | phone_screen | alice bob
Wed afternoon | in_person | alice bob
But this isn't really what I want. The interview candidate only needs to attend two total meetings (one phone_screen
and one in_person
) whereas the above "solution" shows 12. I want to end up with something like this:
Mon morning | phone_screen | bob carl
Mon afternoon | in_person | alice bob
Therefore, we have Requirement (2):
Solving Requirement (2) is trickier for some reason, even though it seems like I should be able to follow a very similar strategy.
## Requirement 2: Ensure only 1 of each type of meeting exists
for meeting in meetings:
per_meeting_data = []
for user in users:
for day in days:
for time in times:
per_meeting_data.append(data[(meeting, day, time, user)])
# Ensure the number of attendees for this meeting type on all days is 2
model.Add(sum(per_meeting_data) == 2)
Adding the above code causes it to fail as an INFEASIBLE
solution. Where am I going wrong?
Upvotes: 0
Views: 180
Reputation: 2766
Shouldn't your first requirement be?
for meeting in meetings:
for day in days:
for time in times:
per_time_data = []
for user in users:
per_time_data.append(data[(meeting, day, time, user)])
# notice the indentation and <=
model.Add(sum(per_time_data) <= 2)
Because it looks like you will have less meetings than slots.
As for your second requirement, I don't quite understand it, do you want each (user, meeting) pair to occur only once?
If so, you can just do:
for meeting in meetings:
for user in users:
per_meeting_data = []
for day in days:
for time in times:
per_meeting_data.append(data[(meeting, day, time, user)])
model.Add(sum(per_meeting_data) == 1)
Upvotes: 1