Python Предупреждение PuLP «Перезаписывает ранее установленную цель» при добавлении ограничений в проблему - PullRequest
0 голосов
/ 26 марта 2020

Я пытаюсь построить систему планирования сотрудников для клини c с использованием библиотеки PuLP. Тем не менее, я получаю предупреждение, упомянутое в заголовке, всякий раз, когда пытаюсь добавить ограничения, как показано в коде ниже. Я использовал код в этом тематическом исследовании , найденном в документации PuLP, в качестве справочного материала для построения проблемы LP.

Краткое описание проблемы: clini c имеет 3 местоположения и Я создаю систему, которая может создать оптимальное решение для планирования списка сотрудников в этих трех местах. Мы не планируем / считаем часы, а просто планируем по дням (например, Джим работает в понедельник, вторник и пятницу). У каждого клини c есть требования к количеству сотрудников определенных c специальностей (которые я называю ролями в коде ниже), необходимых для каждого дня. На данный момент я пытаюсь добавить ограничение, ограничивающее количество сотрудников определенной роли c, которые можно запланировать в определенном месте c определенного дня.

Функция maxnum_ (день, местоположение, роль) на данный момент просто возвращает 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 будет частью расписания. Рассчитанный коэффициент в значительной степени соответствует предпочтениям работника (например, если Джимми Джон не доступен по вторникам, коэффициент для («Джимми Джон», «Вторник», «Клини c A», «Педиатр») составляет отрицательное число, тогда как если бы он был доступен, то это было бы положительное число.) Таким образом, цель состоит в том, чтобы максимизировать эту модель. О, и x - это словарь, отображающий его shift_instance на его LpVariable, а listOfVariables - это список всех возможных tuples / shift_instances.

Мой вопрос: почему я получаю эти предупреждения и почему ограничение не добавляется в LpProblem?

1 Ответ

2 голосов
/ 27 марта 2020

Добрый день!

Вы не должны использовать стандартную sum функцию *1013* для выражения сумм или переменных в целлюлозе. Вы должны использовать функцию 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])
...