python-constraint - schedule maker program does not follow constraints when sum is 0 or 2?

36 views Asked by At

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!!

0

There are 0 answers