Python PuLP 警告 "Overwriting previously set objective" 向问题添加约束时
Python PuLP Warning "Overwriting previously set objective" When Adding Constraints to Problem
我正在尝试使用 PuLP 库为诊所构建员工调度系统。但是,每当我尝试添加约束时,我都会收到标题中提到的警告,如下面的代码所示。我使用 PuLP 文档中 this case study 中的代码作为构建 LP 问题的参考。
问题的快速总结:诊所有 3 个地点,我正在构建一个系统,该系统可以构建最佳解决方案来安排这 3 个地点的员工列表。我们不是 scheduling/counting 小时,而是按天安排(例如吉姆在星期一、星期二和星期五工作)。每个诊所对每天所需的特定专业(我在下面的代码中称为角色)的员工人数都有要求。现在,我正在尝试添加一个限制条件,以限制可以安排在某一天的特定位置的特定角色的员工数量。
函数 maxnum_(day,location,role) 现在只是 returns 3(测试约束)(即可以在任何位置安排的最大员工人数为 3)。当我使用 <=(应该如此)设置约束时,程序完成执行,但是当我打印 shift_model 时,我没有看到任何约束被添加。此外,为了进一步探索问题,我将 <= 更改为 == 和 >=。对于这两种情况,我都收到了覆盖 objective 函数的警告,尽管它看起来不像我...
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 将成为计划的一部分。计算的系数几乎是员工的偏好(例如,如果 Jimmy John 在星期二不可用,则系数 ('Jimmy John', 'Tuesday', "Clinic A', 'Pediatrician' ) 是一个负数,而如果他有空,那么它将是一个正数)。因此 objective 是最大化这个模型。哦,x 是一个字典,将它的 shift_instance 映射到它的LpVariable 和 listOfVariables 是所有可能的列表 tuples/shift_instances.
我的问题是,为什么我会收到这些警告,为什么没有将约束添加到 LpProblem?
美好的一天!
您不应使用 python 的标准 sum
函数 tu 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)
此外,这是关于性能的一般建议,在迭代之前预过滤变量字典会更有效。它还使代码更清晰。以下是编辑后的模型部分:
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])
我正在尝试使用 PuLP 库为诊所构建员工调度系统。但是,每当我尝试添加约束时,我都会收到标题中提到的警告,如下面的代码所示。我使用 PuLP 文档中 this case study 中的代码作为构建 LP 问题的参考。
问题的快速总结:诊所有 3 个地点,我正在构建一个系统,该系统可以构建最佳解决方案来安排这 3 个地点的员工列表。我们不是 scheduling/counting 小时,而是按天安排(例如吉姆在星期一、星期二和星期五工作)。每个诊所对每天所需的特定专业(我在下面的代码中称为角色)的员工人数都有要求。现在,我正在尝试添加一个限制条件,以限制可以安排在某一天的特定位置的特定角色的员工数量。
函数 maxnum_(day,location,role) 现在只是 returns 3(测试约束)(即可以在任何位置安排的最大员工人数为 3)。当我使用 <=(应该如此)设置约束时,程序完成执行,但是当我打印 shift_model 时,我没有看到任何约束被添加。此外,为了进一步探索问题,我将 <= 更改为 == 和 >=。对于这两种情况,我都收到了覆盖 objective 函数的警告,尽管它看起来不像我...
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 将成为计划的一部分。计算的系数几乎是员工的偏好(例如,如果 Jimmy John 在星期二不可用,则系数 ('Jimmy John', 'Tuesday', "Clinic A', 'Pediatrician' ) 是一个负数,而如果他有空,那么它将是一个正数)。因此 objective 是最大化这个模型。哦,x 是一个字典,将它的 shift_instance 映射到它的LpVariable 和 listOfVariables 是所有可能的列表 tuples/shift_instances.
我的问题是,为什么我会收到这些警告,为什么没有将约束添加到 LpProblem?
美好的一天!
您不应使用 python 的标准 sum
函数 tu 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)
此外,这是关于性能的一般建议,在迭代之前预过滤变量字典会更有效。它还使代码更清晰。以下是编辑后的模型部分:
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])