I am trying to create a program that create a schedule automatically based on 6 stores needs and 34 employee's availabilities. I am using python-constraint model to model the problem.
- I wrote 2 constraints (each employee can only work 1 shift per day, each employee must be within their range of requested working days per week.
- I have a dictionary where the values are store objects which contain the name of the store and the schedule.
- The schedule is also a dictionary where the keys are the date in the format
M AM(Monday Morning), where days are [M, T, W, R, F, S, U] and types of shifts are [AM, MID, PM]. - The value of the schedule corresponds to the number of workers needed for said shifts (0, 1, or 2).
- After this step, I will need another constraint to indicate employee's availabilities, but I will cross that bridge once I get there!
The current problem I am having is with constraint #3. Note that constraints 1 and 2 work perfectly and I have isolated constraint 3 for troubleshooting. When I run ONLY constraint 3 and ask for a solution I get None back.
I added a debugging statements which indicates that when the program is tasked to have either 0 or 2 workers assigned to said shift, it always puts 1 no matter what. I am not sure why this happening at all.
If anyone is familiar with python-constraint or even has suggestions on another module compatible with python that could work with this constraints problem, I'd greatly appreciate. Please note my code below as well as a sample output.
My Code
def constrain_model(stores_dict, employees_list, all_possible_shifts):
problem = Problem()
days = ['M', 'T', 'W', 'R', 'F', 'S', 'U']
## Constraint # 3 --> Each store has different shifts with different numbers of employees required on different days
# stores_dict is a dictionary where key is the store name and the value is the store object
# store objects have a name attribute (store.name) and a schedule attribute (store.schedule)
# each store's schedule is a has a key with form f"{day} {shift}" indicating the day (M, T, W, R, F, S, U) and value is the number of people needed for that shift
# some shifts have zero indicating this shift does not exist at this store
# this constraint should enforce that the correct number of workers are assigned to each store's specific shifts
# stores have either 0, 1, or 2 people on shifts as indicated in the value attributes of the entries in the schedule dictionary
# This code does not respond to 0 or 2 workers correctly, but the vars and constraints are correct!
for store in stores_dict.values():
for day in days:
for shift in all_possible_shifts:
assignment_var = f'{store.name}_{day}_{shift}_assigned'
problem.addVariable(assignment_var, [0,1])
for store in stores_dict.values():
for day in days:
for shift in all_possible_shifts:
assignment_var = f'{store.name}_{day}_{shift}_assigned'
def correct_number_of_employees_constraint(*assignments, day=day, shift=shift, store=store):
date = f'{day} {shift}'
print(f'{date} | {store.name} | {assignments} == {store.schedule[date]}')
print(sum(assignments), store.schedule[date])
return sum(assignments) == store.schedule[date]
problem.addConstraint(correct_number_of_employees_constraint, [assignment_var])
return problem
# Runner
stores_dict, employees_list = initialize_all_data()
all_possible_shifts = ["AM", "MID", "PM"]
problem = constrain_model(stores_dict, employees_list, all_possible_shifts)
print("I have the problem")
more = input("Do you want more information?")
if more == 'y':
choice = input("vars or constraints or both")
if choice == 'v' or choice == 'b':
for variable in problem._variables:
print(variable)
print()
if choice == 'c' or choice == 'b':
for constraint in problem._constraints:
print(constraint)
print()
print()
sol = input("Attempt a solution?")
if sol == 'y':
a_solution = problem.getSolution()
print(a_solution)
print(problem.getSolver())
Sample Output:
OUTPUT KEY:
DAY SHIFT | STORE NAME | ASSIGNMENTS | == | REQUIRED NUMBER
CURRENT NUMBER ASSIGNMENTS | REQUIRED NUMBER ASSIGNMENTS
M AM | 87th | (0,) == 2
0 2
M AM | 87th | (1,) == 2
1 2
M MID | 87th | (0,) == 0
0 0
M MID | 87th | (1,) == 0
1 0
M PM | 87th | (0,) == 2
0 2
M PM | 87th | (1,) == 2
1 2
Note that in this output 87th is never assigned 2, only 1 before the program moves on (M AM)
Note that in this output 87th is assigned 1, despite needing 0 (M MID)
Thank you in advance for any help. If you other ideas for implementation in python, please let me know and of course if anyone knows python-constraint well -- please reach out!!