Я создал сценарий Python с использованием библиотеки PuLP для решения проблемы планирования проектов с ограниченными ресурсами для нескольких навыков (MS-RCPSP), но у меня возникают проблемы с назначением задач. Результат, который я получаю, не соответствует ожидаемым результатам, показанным на рис. 1. Можете ли вы помочь мне понять, что происходит не так?
import pulp as pl
# Problem Setup
model = pl.LpProblem("Task_Scheduling", pl.LpMinimize)
# Sets
Nr = range(5) # Tasks
Rr = range(4) # Resources
Sr = range(2) # Skills
Kr = range(4) # Resources skill levels
Ir = range(5) # Tasks
Jr = range(2) # Task skills
M = 20
# Data
Prec = [
[0, 0, 1, 1, 0],
[0, 0, 0, 0, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
]
p = [5, 10, 5, 10, 5] # Duration of each task
R = [
[1, 2],
[1, 0],
[1, 1],
[0, 1],
[1, 1]
]
B = [
[1, 0, 1, 1],
[1, 1, 0, 0]
]
# Decision Variables
x = pl.LpVariable.dicts("x", (Ir, Jr, Kr), cat=pl.LpBinary)
z = pl.LpVariable.dicts("z", (Ir, Ir), cat=pl.LpBinary)
s = pl.LpVariable.dicts("s", Ir, cat=pl.LpContinuous, lowBound=0)
C_max = pl.LpVariable("C_max", lowBound=0, cat=pl.LpContinuous)
# Objective Function
model += C_max, "Minimize_Max_Completion_Time"
# Constraints for max completion time
for j in Nr:
model += C_max >= s[j] + p[j], f"Max_Completion_Time_{j}"
# Skill requirements
for i in Nr:
for j in Sr:
model += pl.lpSum(x[i][j][k] for k in Rr) == R[i][j], f"Skill_Requirement_{i}_{j}"
# Precedence and non-overlap constraints
for i in Nr:
for i_prime in Nr:
if i < i_prime:
model += s[i] + p[i] - M * (1 - z[i][i_prime]) * (1 - Prec[i][i_prime]) <= s[i_prime], f"Precedence_{i}_{i_prime}"
model += z[i][i_prime] + z[i_prime][i] <= 1, f"Non_Overlap_{i}_{i_prime}"
# Resource constraints
for i in Nr:
for k in Rr:
model += pl.lpSum(x[i][j][k] for j in Sr) <= 1, f"One_Skill_Per_Resource_{i}_{k}"
# Resource sharing constraints
for i in Nr:
for i_prime in Nr:
if i < i_prime:
for k in Rr:
model += pl.lpSum(x[i][j][k] for j in Sr) + pl.lpSum(x[i_prime][j][k] for j in Sr) <= 1 + z[i][i_prime] + z[i_prime][i], f"No_Simultaneous_Assignment_{i}_{i_prime}_{k}"
# Skill level matching
for i in Nr:
for j in Sr:
for k in Rr:
model += x[i][j][k] <= B[j][k], f"Skill_Level_Match_{i}_{j}_{k}"
# Solve the model
model.solve()
print("Status:", pl.LpStatus[model.status])
print("Maximum completion time:", pl.value(C_max))
# Display assignment results and timing for each task
for i in Nr:
start_time = pl.value(s[i])
finish_time = start_time + p[i]
print(f"Activity {i+1} starts at time {start_time} and finishes at time {finish_time}.")
for j in Sr:
for k in Rr:
if pl.value(x[i][j][k]) == 1:
print(f"Resource {k+1} is assigned to skill {j+1} for activity {i+1}")
Я не думаю, что вы правильно закодировали ограничение требований к ресурсам (уравнение 2).
Когда я читаю код, он не гарантирует, что ресурс k
обладает навыками j
, и, таким образом, вы получаете незаконное назначение в своих результатах для Действия 4.
Итак, есть 2 способа сделать это....
Первый: умножьте переменную на двоичный параметр из B
вот так:
# Skill requirements
for i in Nr:
for j in Sr:
model += pl.lpSum(x[i][j][k] * B[j][k] for k in Rr) == R[i][j], f"Skill_Requirement_{i}_{j}"
Это линейно, и при проверке можно учитывать потребность в ресурсах только в том случае, если B[j][k]
равно 1.
Второй подход — создать подмножества вашей переменной x
с разными навыками и извлечь из соответствующего подмножества ограничение равенства. Или сделайте это в суммировании с условным выражением, чтобы учитывались только подходящие x
. Чистый эффект тот же. Создание подмножеств может привести к созданию немного более компактной модели.
Когда я сделал вышеописанное, я заметил, что были перекрывающиеся назначения ресурсов... Я отмечаю, что в ваших ограничениях приоритета, я думаю, вы сократили итерацию для основной части, уравнения 3, которое должно быть сделано для всех i, i'
. Переместите условное выражение вниз, чтобы оно работало только со вторым уравнением:
# Precedence and non-overlap constraints
for i in Nr:
for i_prime in Nr:
model += s[i] + p[i] - M * (1 - z[i][i_prime]) * (1 - Prec[i][i_prime]) <= s[i_prime], f"Precedence_{i}_{i_prime}"
if i < i_prime:
model += z[i][i_prime] + z[i_prime][i] <= 1, f"Non_Overlap_{i}_{i_prime}"
Кажется, это работает и дает качественный ответ. Похоже, что существует несколько оптимальных решений (как и должно быть, поскольку ресурсы 3 и 4 взаимозаменяемы), поскольку они немного отличаются, но кажутся законными. Обратите внимание, что я также распечатываю матрицу z
для проверки с помощью этого дополнения к вашему коду:
# Display assignment results and timing for each task
for i in Nr:
start_time = pl.value(s[i])
finish_time = start_time + p[i]
print(f"Activity {i+1} starts at time {start_time} and finishes at time {finish_time}.")
for j in Sr:
for k in Rr:
if pl.value(x[i][j][k]) == 1:
print(f"Resource {k+1} is assigned to skill {j+1} for activity {i+1}")
for ii in Nr:
print(pl.value(z[i][ii]), end = ' ')
print()
print()
Status: Optimal
Maximum completion time: 15.0
Activity 1 starts at time 0.0 and finishes at time 5.0.
Resource 4 is assigned to skill 1 for activity 1
Resource 1 is assigned to skill 2 for activity 1
Resource 2 is assigned to skill 2 for activity 1
0.0 0.0 1.0 1.0 1.0
Activity 2 starts at time 0.0 and finishes at time 10.0.
Resource 3 is assigned to skill 1 for activity 2
0.0 0.0 0.0 0.0 1.0
Activity 3 starts at time 5.0 and finishes at time 10.0.
Resource 4 is assigned to skill 1 for activity 3
Resource 1 is assigned to skill 2 for activity 3
0.0 0.0 0.0 0.0 1.0
Activity 4 starts at time 5.0 and finishes at time 15.0.
Resource 2 is assigned to skill 2 for activity 4
0.0 0.0 0.0 0.0 0.0
Activity 5 starts at time 10.0 and finishes at time 15.0.
Resource 4 is assigned to skill 1 for activity 5
Resource 1 is assigned to skill 2 for activity 5
0.0 0.0 0.0 0.0 0.0
Буду очень благодарен !!! @AirSquid
Большое вам спасибо, сэр. Подскажите, пожалуйста, проблема в модели или в коде? Еще раз спасибо!