相关文章:
《【柔性作业车间调度FJSP】通过python调用OR-Tools的区间变量建模求解》
《【(较大规模)作业车间调度JSP】通过OR-Tools的区间变量建模求解的效率对比实验》
柔性作业车间调度(Flexible Job Shop Scheduling Problem,FJSP)相比于 JSP问题,增加了工序的可选设备不唯一的条件,这增加了问题的柔性,也增大了问题的求解难度。求解FJSP问题时,不仅需要确定工件工序分配的机器,还需要确定机器上工序的加工顺序,以最优化既定的目标。
在关于JSP的大规模排程问题上的实验发现,设备的可加工工序总时间的方差对求解的Gap的收敛效率有影响,当方差很大时,设备的负载可能很不平衡,此时存在较为明显的瓶颈设备,则JSP问题在求解时Gap值收敛效率较高。因此,本文进一步探究这个规律在FJSP问题当中是否存在。
基于 Brandimarte 数据集进行测试,其中包含了 Nk01.fjs ~ Mk10.fjs
算例文件,打印输出工序数(表征问题规模)、工序平均可上机台数(柔性表征问题求解复杂度)、设备可加工工序的总时间及其方差,以及输出各个算例的求解情况(设置求解终止条件为 10s/5%Gap)。
同样的,以下代码基于FJSP标准测试数据解析的结果进行建模。
model = cp_model.CpModel()
# 命名元组:存储变量信息(相当于实例化一道工序)
task_type = collections.namedtuple("task_type", "start end interval")
# 存储变量信息
all_tasks = {}
x_ijk = {}
y_ijlmk = {}
real_tasks_s = {}
real_tasks_e = {}
# 将工序信息按可上机器(一台)进行存储
machine_to_intervals = collections.defaultdict(list)
for job_id in range(Job_num):
for task_id in range(job_2_step_num[job_id]):
real_tasks_s[job_id, task_id] = model.NewIntVar(0,1000,"start_by_task")
real_tasks_e[job_id, task_id] = model.NewIntVar(0,1000,"end_by_task")
for machine_id in job_step_2_avail_machines[job_id, task_id]:
suffix = f"_{job_id}_{task_id}_{machine_id}"
# 工序选择了哪一台设备
x_ijk[job_id, task_id, machine_id] = model.NewBoolVar(name="x"+suffix)
duration_init = job_step_machine_2_ptime[job_id, task_id, machine_id]
duration = model.NewIntVar(0, 1000, "duration" + suffix)
model.Add(duration <= duration_init + 1000*(1-x_ijk[job_id, task_id, machine_id]))
model.Add(duration >= duration_init + 1000*(x_ijk[job_id, task_id, machine_id]-1))
model.Add(duration <= 1000*x_ijk[job_id, task_id, machine_id])
start_var = model.NewIntVar(0, 1000, "start" + suffix)
end_var = model.NewIntVar(0, 1000, "end" + suffix)
model.Add(start_var + end_var <= 1000*x_ijk[job_id, task_id, machine_id])
interval_var = model.NewIntervalVar(
start=start_var, size=duration, end=end_var, name="interval" + suffix)
all_tasks[job_id, task_id, machine_id] = task_type(
start=start_var, end=end_var, interval=interval_var)
machine_to_intervals[machine_id].append(interval_var)
for job_id2 in range(Job_num):
for task_id2 in range(job_2_step_num[job_id]):
if not( (job_id==job_id2) and (task_id==task_id2) ):
if machine_id in job_step_2_avail_machines[job_id2, task_id2]:
y_ijlmk[job_id, task_id, job_id2, task_id2, machine_id]=\
model.NewBoolVar(name="y")
Cmax = model.NewIntVar(0, 1000, "Cmax")
## 工序有且只能选择一个机台
for job_id in range(Job_num):
for task_id in range(job_2_step_num[job_id]):
model.Add(sum(x_ijk[job_id, task_id, k] for k in job_step_2_avail_machines[job_id, task_id]) == 1)
## 对齐设备坑位上的时间与工序的时间
for job_id in range(Job_num):
for task_id in range(job_2_step_num[job_id]):
model.AddMaxEquality(real_tasks_s[job_id, task_id],
[all_tasks[job_id, task_id, machine_].start for machine_ in job_step_2_avail_machines[job_id, task_id]])
model.AddMaxEquality(real_tasks_e[job_id, task_id],
[all_tasks[job_id, task_id, machine_].end for machine_ in job_step_2_avail_machines[job_id, task_id]])
## 设备上区间变量的不重叠约束
for machine in machine_to_intervals:
model.AddNoOverlap(machine_to_intervals[machine])
## 工件的前后工序约束
for job_id in range(Job_num):
for task_id in range(job_2_step_num[job_id]-1):
model.Add(real_tasks_s[job_id, task_id + 1] >= real_tasks_e[job_id, task_id])
model.AddMaxEquality(Cmax, [real_tasks_e[index] for index in real_tasks_e])
model.Minimize(Cmax)
solver = cp_model.CpSolver()
status = solver.Solve(model, VarArraySolutionPrinter(10, 0.05))
print(f"Optimal Schedule Length: {solver.ObjectiveValue()}")
print("\nStatistics")
print(f" - conflicts: {solver.NumConflicts()}")
print(f" - branches : {solver.NumBranches()}")
print(f" - wall time: {solver.WallTime()}s")
在FJSP问题提出相同的猜想1:即问题规模相近情况下,若设备的可加工工序总时间的方差越大,则该问题求解的Gap值收敛效率更高。根据以下测试数据的结果进行分析。
下面数据当中的 “opt” 的数值来源于一些期刊文献的较优结果,并不一定是算例的最优解。
MK01.fjs 的结果数据:
总的工序数 55
设备可加工工序总时长的方差 568.8
当前解:40 # opt: 40
求解时间: 0.3088191s
Gap: 0.0
MK02.fjs 的结果数据:
总的工序数 58
设备可加工工序总时长的方差 213.36666666666667
当前解: 27.0 # opt: 26
求解时间: 11.130754476s
Gap: 0.038461538461538464
易知,MK01 和 MK02 规模相近,前者的设备可加工工序总时间方差较大,因此Gap的收敛效率远高于后者。符合我们的猜想。
MK03.fjs 的结果数据:
总的工序数 150
设备可加工工序总时长的方差 8341.839285714286
当前解: 214.0 # opt: 204
求解时间: 6.5982534390000005s
Gap: 0.049019607843137254
MK06.fjs 的结果数据:
总的工序数 150
设备可加工工序总时长的方差 4061.904761904762
当前解: 93.0 # opt: 69
求解时间: 11.204710798s
Gap: 0.9375
易知,MK03 和 MK06 的结果数据也符合我们的猜想。
MK04.fjs 的结果数据:
总的工序数 90
设备可加工工序总时长的方差 3893.4761904761904
当前解: 63.0 # opt: 60
求解时间: 0.7978846620000001s
Gap: 0.05
MK05.fjs 的结果数据:
总的工序数 106
设备可加工工序总时长的方差 3220.3333333333335
当前解: 180.0 # opt: 172
求解时间: 6.609042677000001s
Gap: 0.046511627906976744
MK07.fjs 的结果数据:
总的工序数 100
设备可加工工序总时长的方差 8147
当前解: 151.0 # opt: 146
求解时间: 11.379868928s
Gap: 0.09420289855072464
易知,MK04 和 MK05 的结果数据符合我们的猜想,但是 MK07 的数据规模比 MK05 的小,且方差大更多,求解效率却不占优。因此,我们围绕我们的猜想进一步讨论下方差以外的信息。
打印出MK07的设备的可加工工序总时间:
{1: 231, 2: 189, 3: 334, 4: 249, 5: 87}
而MK04的相应数据为:
{1: 188, 2: 24, 3: 6, 4: 58, 5: 30, 6: 57, 7: 14}
明显的,可以发现MK04的可加工工序总时间中,有唯一的显著的瓶颈设备(1),而这在MK07的数据中,可加工工序总时间领先的比例不大。因此我们进一步猜想2:若“瓶颈设备”的可加工总时长领先的比例越大,即瓶颈越明显,求解效率才会有较大的提升,反之,即使有较大的方差也对求解效率的提升有限。
因此,我们对前面的算例以及后续的算例,都增加分析“瓶颈”设备的可加工工序的总时长的领先第2名的设备的比例,该比例值越大,则说明瓶颈设备越明显。
max__ = 0
min_ratio_ = 1
for m in new1:
if new1[m] > max__:
max__ = new1[m]
for m in new1:
if new1[m] == max__:
continue
if (max__ - new1[m])/max__ < min_ratio_:
min_ratio_ = (max__ - new1[m])/max__
print(min_ratio_)
# MK01:0.222
# MK02:0.184
# MK03:0.063
# MK04:0.691
# MK05:0.119
# MK06:0.391
# MK07:0.254
根据MK01和MK02的瓶颈领先比例可知,MK01的瓶颈更显著,求解结果符合猜想2的预期。MK03和MK06的结果,两者并不呈现谁支配谁的关系(若方差更大,且瓶颈领先比例更大视为支配),因此并不能支持猜想2。MK04和MK05的结果能证明猜想2,但MK05和MK07的数据结果却与猜想2相悖,经分析发下在MK05的工序平均会在1.5台设备间进行选择,而MK07的工序平均会在3台设备间进行选择,因此两者在求解规模上并不相同,后者显然更复杂(柔性更大)。
因此MK07的结果并不完全否定猜想2,只是在比较问题规模,并不能简单地通过问题中的工序总数进行判断,而应结合考虑问题的柔性。所以,接下来比较的MK09和MK10两个算例的工序总数相等,且工序的平均可选设备数都为3。
MK09.fjs 的结果数据:
总的工序数 240
设备可加工工序总时长的方差 23662.48888888889
当前解: 397.0 # opt: 307
求解时间: 11.159340499s
Gap: 0.3277591973244147
瓶颈领先比例:0.31095406360424027
MK10.fjs 的结果数据:
总的工序数 240
设备可加工工序总时长的方差 20955.61111111111
当前解: 341.0 # opt: 220
求解时间: 11.237421519000002s
Gap: 0.8839779005524862
瓶颈领先比例:0.018907563025210083
上述MK09和MK10的结果数据支持我们的猜想2。
综上所述:我们可以通过以下的办法粗略地判断FJSP问题的求解效率,先通过问题当中的工序总数,以及工序的平均可上设备数(柔性)判断不同问题之间的规模大小,若在规模相近的前提下,可以查看数据当中是否存在显著的瓶颈设备(即有大量工序竞争的设备资源视为瓶颈),若存在,则该问题在最优性验证处的效率会极高。