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

时间:2020-03-26 04:22:30

标签: python constraints linear-programming pulp

我正在尝试使用PuLP库为诊所建立员工排班系统。但是,每当尝试添加约束时,都会不断收到标题中提到的警告,如下面的代码所示。我使用了PuLP文档中this case study中的代码作为构建LP问题的参考。

问题的快速摘要:诊所有3个位置,我正在构建一个系统,该系统可以构建最佳解决方案来安排这3个位置的员工列表。我们不是在计划/计算时间,而是在按天计划(例如,吉姆在星期一,星期二和星期五工作)。每个诊所都对每天所需的特定专业的员工人数(在下面的代码中称为角色)有要求。现在,我正在尝试添加一个限制,该限制限制可以在特定日期的特定位置安排的特定角色的员工数量。

函数maxnum_(day,location,role)现在仅返回3(以测试约束)(即,可以在任何位置安排的最大员工人数为3)。当我使用<=(应该是)设置约束时,程序会完成执行,但是当我打印shift_model时,看不到任何约束被添加。此外,为了尝试进一步探讨该问题,我将<=更改为==,并将> =更改为。对于这两种情况,我都收到了覆盖目标函数的警告,即使看起来好像不是...

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])

shift_model是元组(e,d,l,r)的总和乘以计算出的系数。元组是“ shift_instance”,其中雇员e在第d天在位置l的角色r工作。这些元组是问题的变量,可以为0或1,其中1表示shift_instance将成为计划的一部分。计算出的系数几乎是员工的偏好(例如,如果吉米·约翰在星期二不在家,则该系数(“吉米·约翰”,“星期二”,“诊所A”,“儿科医生”)为负数,如果他有空,那么它将是一个正数。)因此,目标是最大化该模型。哦,x是将其shift_instance映射到其LpVariable的字典,而listOfVariables是所有可能的元组/ shift_instances的列表

我的问题是,为什么我会收到这些警告?为什么没有将约束添加到LpProblem中?

1 个答案:

答案 0 :(得分:2)

美好的一天!

您不应该在纸浆中使用python的标准local:int(xs:int(1))函数tu sum表达式或变量。您应该使用软件包提供的sum函数。它不仅效率更高,而且在这种情况下,它可以解决您的问题。不过,我无法解释原因。

因此,在约束代码中,您应该具有:

lpSum

此外,这是有关性能的一般建议,在迭代之前预先过滤变量字典会更有效。这也使代码更整洁。下面是编辑后的模型部分:

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)