简体   繁体   English

向问题添加约束时,Python PuLP 警告“覆盖先前设置的目标”

[英]Python PuLP Warning "Overwriting previously set objective" When Adding Constraints to Problem

I am trying to build an employee scheduling system for a clinic using the PuLP library.我正在尝试使用 PuLP 库为诊所构建员工调度系统。 However, I keep getting the warning mentioned in the title whenever I try adding constraints as shown in the code below.但是,每当我尝试添加约束时,我都会收到标题中提到的警告,如下面的代码所示。 I used the code in this case study found in the PuLP documentation as a reference to build the LP problem.我使用在 PuLP 文档中找到的本案例研究中的代码作为构建 LP 问题的参考。

A quick summary of the problem: The clinic has 3 locations and I'm building a system that can build an optimal solution to scheduling a list of employees across these 3 locations.问题的快速总结:诊所有 3 个地点,我正在构建一个系统,该系统可以构建最佳解决方案来安排这 3 个地点的员工列表。 We are not scheduling/counting hours, but rather just scheduling by days (eg Jim works on Monday, Tuesday, and Friday).我们不安排/计算小时数,而只是按天安排(例如,Jim 在星期一、星期二和星期五工作)。 Each clinic has requirements for the number of employees of specific specialties (which I call roles in the code below) needed for each day.每个诊所对每天所需的特定专业(我在下面的代码中称之为角色)的员工人数都有要求。 For now, I am trying to add a constraint that limits the number of employees of a specific role that can be scheduled at a specific location of a certain day.现在,我正在尝试添加一个约束来限制可以在某一天的特定位置安排的特定角色的员工数量。

The function maxnum_(day,location,role) for now just returns 3 (to test the constraint)(ie the max number of employees that can be scheduled at any location is 3).函数 maxnum_(day,location,role) 现在只返回 3(以测试约束)(即可以在任何位置安排的最大员工数为 3)。 When I set the constraint using <= (as it should be) the program completes execution, but when I print the shift_model, I do not see any of the constraints being added.当我使用 <= (应该如此)设置约束时,程序完成执行,但是当我打印 shift_model 时,我没有看到任何添加的约束。 Furthermore, in attempt of trying to further explore the problem, I changed <= to == and the >=.此外,为了尝试进一步探索该问题,我将 <= 更改为 == 和 >=。 For both cases, I received the warning of overwriting the objective function even though it does not seem like I am...对于这两种情况,我都收到了覆盖目标函数的警告,即使它看起来不像我......

from pulp import *
def maxnum_(d,l,r):
    return 3
def coefficients(instance):

    #Given a shift instance, returns the weight of the preference
    #based on the parameters of the instance.
    #Chosen values are subject to change.

    weight = 0
    employee = instance[0]
    day = instance[1]
    location = instance[2]
    role = instance[3]
    global allEmployees
    if day not in allEmployees[employee]['Availability']:
        weight -= 5
    else:
        weight += 1
    if location not in allEmployees[employee]['PreferredLocationOfWork']:
        weight -= 2
    else:
        weight+=1
    return weight


shifts = ['M1','M2','T1','T2','W1','W2','R1','R2','F1','F2']
allEmployees = {'Billy Bob': {'License': 'Nurse Practitioner', 'Specialty': 'Urgent', 'Age': 'Geriatric', 'Availability': ['M1', 'T1', 'F1', 'M2', 'R2', 'F2'], 'PreferredLocationOfWork': 'PPHC', 'Requested_dates_off': ['2020-05-09', '2021-01-31', 'YYYY-MM-DD']}, 'Jimmy John': {'License': 'Physician', 'Specialty': 'Emergency', 'Age': 'Pediatric', 'Availability': ['T1', 'F1', 'W2', 'R2'], 'PreferredLocationOfWork': 'CHCF', 'Requested_dates_off': ['2020-05-09', '2021-01-31', 'YYYY-MM-DD']}}
# Ignoring specialty/age/license required and min number employees required for now, will implement later
allLocations = {'CHCF': {'MinNumberEmployeesRequiredPresent': 1, 'SpecialtyRequired': ['Emergency', 'Urgent', 'Urgent'], 'AgeRequired': ['Pediatric', 'Geriatric', 'Pediatric'], 'LicenseRequired': ['Physician', 'NurseMidwife', 'NursePracticioner']}, 'THS': {'MinNumberEmployeesRequiredPresent': 1, 'SpecialtyRequired': ['Emergency', 'Urgent', 'Primary', 'Obstetrics'], 'AgeRequired': ['Pediatric', 'Geriatric', 'Family', 'Adult'], 'LicenseRequired': ['Physician', 'NurseMidwife', 'NursePracticioner', 'Physician']}, 'PPHC': {'MinNumberEmployeesRequiredPresent': 1, 'SpecialtyRequired': ['Urgent', 'Urgent', 'Urgent'], 'AgeRequired': ['Geriatric', 'Geriatric', 'Pediatric'], 'LicenseRequired': ['Physician', 'NurseMidwife', 'NursePracticioner']}}
age_ = ['Pediatric', 'Adult','Geriatric', 'Family']
specialty_ = ['Emergency', 'Urgent Care', 'Primary Care', 'Obstetrics']
license_ = ['Physician','Nurse Midwife', 'Nurse Practitioner']
roles = [','.join([a,s,l]) for a in age_ for s in specialty_ for l in license_ ]
listOfVariables = []
# Start creating the tuples of shift instances, these will be the variables of our LP problem
for employee in allEmployees.keys():
    specialty = []
    specialty.append(allEmployees[employee]['Age'])
    specialty.append(allEmployees[employee]['Specialty'])
    specialty.append(allEmployees[employee]['License'])
    specialtyString = ','.join(specialty)
    #TODO: Implement weighted alternates...

    for day in shifts:
        # Include all locations, set preferred location coefficient to 10 and non preferred to 1?
        for location in allLocations.keys():
            # In coefficients, set days not in preference to 1, all preferred dates to 10
            listOfVariables.append((employee, day, location, specialtyString))

x = LpVariable.dicts('shift', listOfVariables, lowBound = 0, upBound = 1, cat = LpInteger)

shift_model = LpProblem("Employee_Scheduling_Model" , LpMaximize)
# The objective function
shift_model += sum([coefficients(shift_instance) * x[shift_instance] for shift_instance \
            in listOfVariables])

# Add Constraint limiting the number of possible employees of a specific role to schedule on a given day at a given location
for day in shifts: # for each day in pay period
    for location in allLocations.keys(): # for each clinic
        for role in roles: # for each role
            shift_model += sum([x[shift_instance] for shift_instance in listOfVariables if day in shift_instance and location in shift_instance\
                    and role in shift_instance]) == maxnum_(day, location, role), "Max employees for {} {} {}".format(day,location,role)

shift_model.solve()
print("Optimal employee schedule: ")
for shift_instance in listOfVariables:
    if x[shift_instance].value() == 1.0:
        print(x[shift_instance])

The shift_model is a summation of tuples (e, d, l, r) multiplied with a calculated coefficient. shift_model 是元组 (e, d, l, r) 乘以计算系数的总和。 The tuple is a "shift_instance" where employee e works on day d at location l for role r.该元组是一个“shift_instance”,其中员工 e 在第 d 天在位置 l 为角色 r 工作。 These tuples are the variables of the problem and can either be 0 or 1, where 1 indicates that the that shift_instance will be part of the schedule.这些元组是问题的变量,可以是 0 或 1,其中 1 表示该 shift_instance 将成为计划的一部分。 The coefficient calculated is pretty much the preferences of the employee (eg if an Jimmy John is not available on Tuesdays, the the coefficient for ('Jimmy John', 'Tuesday', "Clinic A', 'Pediatrician') is a negative number, whereas if he was available, then it would be a positive number). Thus the objective is to maximize this model. Oh, and x is a dictionary mapping its shift_instance to its LpVariable and listOfVariables is a list of all the possible tuples/shift_instances.计算的系数几乎是员工的偏好(例如,如果 Jimmy John 在星期二不可用,则 ('Jimmy John', 'Tuesday', "Clinic A', 'Pediatrician') 的系数是负数,而如果他有空,那么它将是一个正数。因此目标是最大化这个模型。哦,x 是一个字典,将它的 shift_instance 映射到它的 LpVariable 和 listOfVariables 是所有可能的元组/shift_instances 的列表.

My question is, why am I getting these warnings and why is the constraint not being added to the LpProblem?我的问题是,为什么我会收到这些警告以及为什么没有将约束添加到 LpProblem 中?

Good day!再会!

You should not use python's standard sum function tu sum expressions or variables in pulp.您不应该在纸浆中使用 python 的标准sum函数 tu sum 表达式或变量。 You should be using the lpSum function provided by the package.您应该使用包提供的lpSum函数。 It's not only more efficient but, in this case, it solves your issue.它不仅效率更高,而且在这种情况下,它可以解决您的问题。 I'm unable to explain why, though.不过,我无法解释原因。

So, in the code of the constraint you should have:所以,在约束的代码中你应该有:

shift_model += lpSum([coefficients(shift_instance) * x[shift_instance] for shift_instance \
            in listOfVariables])

# Add Constraint limiting the number of possible employees of a specific role to schedule on a given day at a given location
for day in shifts: # for each day in pay period
    for location in allLocations.keys(): # for each clinic
        for role in roles: # for each role
            shift_model += lpSum([x[shift_instance] for shift_instance in listOfVariables if day in shift_instance and location in shift_instance\
                    and role in shift_instance]) == maxnum_(day, location, role), "Max employees for {} {} {}".format(day,location,role)

Also, and this is a general recomendation about performance, it's more efficient to pre-filter your dictionary of variables before iterating.此外,这是关于性能的一般建议,在迭代之前预过滤变量字典更有效。 It also makes for cleaner code.它还使代码更清晰。 Below is the edited model part:以下是编辑后的模型部分:


x = LpVariable.dicts('shift', listOfVariables, lowBound = 0, upBound = 1, cat = LpInteger)

shift_model = LpProblem("Employee_Scheduling_Model" , LpMaximize)
# The objective function
shift_model += lpSum([coefficients(shift_instance) * x[shift_instance] for shift_instance \
            in listOfVariables])

# for each day, location and role: a list of variables
x_dlr = {}
for (e, d, l, r), _x in x.items():
    _tup = d, l, r
    if _tup not in x_dlr:
        x_dlr[_tup] = []
    x_dlr[_tup].append(_x)

# Add Constraint limiting the number of possible employees of a specific role to schedule on a given day at a given location
for day in shifts: # for each day in pay period
    for location in allLocations.keys(): # for each clinic
        for role in roles: # for each role
            _tup = day, location, role
            shift_model += lpSum(x_dlr.get(_tup, [])) == maxnum_(*_tup), "Max employees for {} {} {}".format(day,location,role)

shift_model.solve()
print("Optimal employee schedule: ")
for shift_instance in listOfVariables:
    if x[shift_instance].value() == 1.0:
        print(x[shift_instance])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM