Search code examples
python-3.xfor-loopconstraintsschedulingpulp

how to put any number of consecutive variables in a for loop for pulp constraint?


I need help to tackle this coding problem. I have a very simple pulp scheduling problem. It has a solution, but the solution is missing 2 constraints that I don't even know how to begin. This post, I just like to tackle one. If someone can point out a direction, or other examples, at least I can read more about it. I did look into itertool, but I don't think that will help because it would require if--elif condition, which i know is not possible with Lineear optimization. here is the code:

import pulp

Days= ["Monday"]
Employees =["Paul", "Beth", "Tom"]
Hours = ["9am - 10am", "10am - 11am", "11am - 12pm", "12pm - 1pm", "1pm - 2pm", "2pm - 3pm", "3pm - 4pm", "4pm - 5pm", "5pm - 6pm",  "6pm - 7pm", "7pm - 8pm", "8pm - 9pm" ]

prob=pulp.LpProblem("Hours maximizing problem", pulp.LpMaximize)

avail = pulp.LpVariable.dicts("var", ((employee, day, hour) for employee in Employees for day in Days for hour in Hours), cat="Binary")

# each employee has their desire working # of hours. #
prob += pulp.lpSum(avail['Paul', day, hour] for day in Days for hour in Hours)== 2
prob += pulp.lpSum(avail['Beth', day, hour] for day in Days for hour in Hours)<= 5
prob += pulp.lpSum(avail['Tom', day, hour] for day in Days for hour in Hours)<= 5

#each hour only needs one person
for hour in Hours:
    for day in Days:
        for employee in Employees:
            prob += pulp.lpSum(avail[employee, day, hour] for employee in Employees)==1


prob += 0, "Objective Function"
prob.writeLP("add to 2.lp")
prob.solve()
print (prob)

the constraint that is missing is that all hours worked must be consecutive. as an example: Tom, Monday work 4pm to 9pm, or any combination of 5 hours, but it needs to be together, instead of one hour in the morning(as an example), and 4 other hours in the afternoon.

any help would be appreciated.


Solution

  • You could introduce binary variables to track whether a shift is started/ended in a particular hour, and intorduce a constraint that there can only be one shift start during each day:

    import pulp
    
    Days= ["Monday"]
    Employees =["Paul", "Beth", "Tom"]
    Hours = [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
    
    prob=pulp.LpProblem("Hours maximizing problem", pulp.LpMaximize)
    
    avail = pulp.LpVariable.dicts("var", ((employee, day, hour) for employee in Employees for day in Days for hour in Hours), cat="Binary")
    
    startShift = pulp.LpVariable.dicts("start", ((employee, day, hour) for employee in Employees for day in Days for hour in Hours), cat="Binary")
    endShift = pulp.LpVariable.dicts("end", ((employee, day, hour) for employee in Employees for day in Days for hour in Hours), cat="Binary")
    
    # each employee has their desire working # of hours. #
    prob += pulp.lpSum(avail['Paul', day, hour] for day in Days for hour in Hours)== 2
    prob += pulp.lpSum(avail['Beth', day, hour] for day in Days for hour in Hours)<= 5
    prob += pulp.lpSum(avail['Tom', day, hour] for day in Days for hour in Hours)<= 5
    
    #each hour only needs one person
    for hour in Hours:
        for day in Days:
            prob += pulp.lpSum(avail[employee, day, hour] for employee in Employees)==1
    
            for employee in Employees:
                if hour == Hours[0]:
                    # to be working in the first hour of the day, avail==1
                    # they must have started their shift, startShift==1
                    prob += avail[employee, day, hour] == startShift[employee, day, hour]
                else:
                    # to be working in any other hour of the day, they either
                    # have to already be part through their shift; avail[e, d, h-1]
                    # or they have to have started their shift in this hour; startShift[e, d, h]
                    # also, if they just ended their shift, they are no longer working; endShift[e, d, h-1]
                    prob += avail[employee, day, hour] == avail[employee, day, hour-1] + startShift[employee, day, hour] - endShift[employee, day, hour-1]
    
    # each employee must have onlhy a single shift
    for employee in Employees:
        for day in Days:
            prob += pulp.lpSum([startShift[employee, day, hour] for hour in Hours]) == 1
            prob += pulp.lpSum([endShift[employee, day, hour] for hour in Hours]) == 1
    
    
    prob += 0, "Objective Function"
    prob.writeLP("add to 2.lp")
    prob.solve()
    print (prob)
    
    for v in prob.variables():
        print (v.name, "=", v.varValue)
    
    print('Status:',pulp.LpStatus[prob.status])