量子退火Python实战(2):护士调度问题(NSP : Nurse Scheduling Problem)

薛定谔了么
2024-11-27 14:57:52
本帖最后由 薛定谔了么 于 2025-1-21 15:33 编辑

前言:关于调度问题(Scheduling Problem)

调度就是人和机器的调度和排序。由于许多人的日程安排相互交织,还要考虑机器的运行状态等因素,实际业务中出现的日程安排极其复杂。

手动排程时,熟悉现场知识的老手通常要花几个小时来规划一个小规模的项目,甚至可能需要几天时间。但是,如果我们可以把这些问题建模成 组合优化问题,这样便能在几分钟到几十分钟内自动设置复杂的调度。实际的工业界,有工人的工作时间调度机器的使用时间调度

护士调度问题(NSP)

-下面的论文里可知,NSP 从 1969 年之前就开始被研究,被认为是 NP-hard问题。作为现实生活中常见调度问题,我们会有很多约束,最主要的就是轮班约束护士约束。本文将介绍护士调度问题中,一些常见约束如何建模为QUBO。

Solos, Ioannis; Tassopoulos, Ioannis; Beligiannis, Grigorios (21 May 2013). “A Generic Two-Phase Stochastic Variable Neighborhood Approach for Effectively Solving the Nurse Rostering Problem”. Algorithms. 6 (2): 278–308.doi:10.3390/a6020278.
 

一、护士调度问题(NSP)的QUBO建模

 

提示:本章参考了https://qard.is.tohoku.ac.jp/T-Wave/?p=1756,是日本东北大学专门研究量子退火研究室的学生写的量子退火的各种论文解析,这次解析的论文如下:

 

Title : Application of Quantum Annealing to Nurse Scheduling Problem Author : Kazuki Ikeda, Yuma Nakamura & Travis S.Humble Reference : Scientific Reports 9, Article number: 12837 (2019) https://doi.org/10.1038/s41598-019-49172-3

下面是一个简单的,4个护士,3个工作日的排班示例,我们称下面的表格为排班矩阵

我们发现右边的排班,会导致护士3没有被安排工作,这是现实生活中不允许的,接下来讲目标变量和约束条件的建模。

1.目标变量

2.约束条件定义

因为护士调度问题不存在直接最小化的目标函数,只需要排班矩阵满足以下约束即可。

在 NSP 中可以想象各种约束示例。例如,可以完成的工作量(劳动力)取决于护士的经验。因此,在工作量大的日子里,不可能只有新人在一起工作。因此,我们需要【保证每天所需的劳动力】这一约束条件。可以考虑许多其他约束,但本文作为示例,定义了以下三个约束:

约束a. 确保没有护士连续工作超过 2 天(硬约束

约束b. 确保每天都有所需的劳动力(硬约束

约束c. 可以根据护士的忙碌程度调整上班的次数(软约束

这里把约束分为硬约束和软约束。硬约束是必须满足的约束。另一方面,软约束是不必满足但需要满足的约束

为了描述这些约束,需要的变量如下表所示:

★变量的注解:

每个约束对应的QUBO项如下:

上面的约束全部满足的话,总哈密顿量的值为0。

3.【约束a】的补充说明

其实我们也可以不使用矩阵J来表示该约束,可以用下面的式子替代:

4.【约束c】的补充说明

约束c的,,通过下面的实例来帮助大家理解。

上面的具体值代入后:

通过该项,我们可以通过每个护士的繁忙程度来优化每个人的出勤时间。

二、Python实现NSP的QUBO

本章参考了https://qard.is.tohoku.ac.jp/T-Wave/?p=2764,日本人经常会把实验结果可视化出来,值得借鉴。

1.参数设定表

因为NSP问题需要先设定一些参数的初始值,所以整理称表格如下:

from itertools import product
import numpy as np
from itertools import product
from pyqubo import Array, Constraint, Placeholder
from neal import SimulatedAnnealingSampler

N = 3  # 护士人数
D = 14  # 待排班日数
a = 7 / 2  # 连续工作2天以上的惩罚值
F = [4, 5, 5]  # 每个护士希望的出勤日数

2.QUBO实现

惩罚项矩阵J:

J = np.zeros((N, D, N, D))
for n1, d1, n2, d2 in product(range(N), range(D), range(N), range(D)):
    if n1 == n2 and d1 + 1 == d2:
        J[n1, d1, n2, d2] = a

总哈密顿量H

# binary变量
q = Array.create('q', shape=(N, D), vartype='BINARY')

# 连续工作2天以上的惩罚项矩阵
H1 = np.sum([J[n1, d1, n2, d2] * q[n1, d1] * q[n2, d2]
             for n1, n2, d1, d2 in product(range(N), range(N), range(D), range(D))])

# 确保每天d都有一个人的劳动力
H2 = np.sum([(np.sum([q[n,d] for n in range(N)]) - 1)**2 for d in range(D)])

# 确保全员出勤次数相等
H3 = np.sum([(np.sum([q[n,d] for d in range(D)]) - F[n])**2 for n in range(N)])

# 最小化的目标哈密顿量H
H = Placeholder('alpha') * Constraint(H1, 'H1') + Placeholder('lam') * Constraint(H2, 'H2') + Placeholder('gamma') * H3
model = H.compile()

退火算法采样

#这次采用neal的模拟退火
from neal import SimulatedAnnealingSampler
feed_dict = {'alpha': 1.0, 'lam': 1.3, 'gamma': 0.3} # 制約項の係数
bqm = model.to_bqm(feed_dict=feed_dict)
sampler = SimulatedAnnealingSampler()

# D-Wave的量子退火机可以这么使用
#from dwave.system import DWaveSampler, EmbeddingComposite
#sampler_config = {'solver': 'DW_2000Q_6', 'token': 'YOUR_TOKEN'}
#sampler = EmbeddingComposite(DWaveSampler(**sampler_config))

num_reads = 1000
sampleset = sampler.sample(bqm, num_reads=num_reads)
sampleset.record[:10]

输出结果如下:

sampleset.record有以下几部分:

– 解的状态

– 能力值

– 解的个数

rec.array([([1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0], 4.76063633e-13, 1),
           ([0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1], 4.76063633e-13, 1),
           ([1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1], 4.68958206e-13, 1),
           ([0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0], 4.68958206e-13, 1),
           ([1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1], 4.68958206e-13, 1),
           ([0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0], 4.76063633e-13, 1),
           ([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0], 4.68958206e-13, 1),
           ([0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1], 4.76063633e-13, 1),
           ([0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0], 4.76063633e-13, 1),
           ([0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0], 4.68958206e-13, 1)],
          dtype=[('sample', 'i1', (42,)), ('energy', '<f8'), ('num_occurrences', '<i8')])

3.实验结果检验和可视化

检查解是否满足约束:

def extract_feasible_samples(samples, print_broken=False):
    feasible_samples = []
    for sample in decoded_samples:
        constraints = sample.constraints(only_broken=True)
        if len(constraints) == 0:
            feasible_samples.append(sample)
        elif print_broken:
            print(constraints)
    return feasible_samples

decoded_samples = model.decode_sampleset(sampleset.aggregate(), feed_dict)
feasible_samples = extract_feasible_samples(decoded_samples)
print('满足约束的解的个数:', len(feasible_samples))

输出结果:

满足约束的解的个数: 894
import matplotlib.pyplot as plt

lowest_sample = feasible_samples[0].sample

schedules = np.zeros((N, D))
for n in range(N):
    for d in range(D):
        if lowest_sample[f'q[{n}][{d}]'] == 1:
            schedules[n, d] = 1

plt.imshow(schedules, cmap="Greys")
plt.xlabel('D')
plt.ylabel('N')
plt.show()

打印出的结果如下:

print('实际的工作日数:', np.sum(schedules, axis=1))
print('希望的工作日数:', F)
实际的工作日数:[4. 5. 5.]
希望的工作日数:[4, 5, 5]

总结

重复实验的这位学生的最后总结如下:

令人失望的是,我无法为多达 56 个变量的问题找到一个好的基态解决方案。通过执行比本文更详细的系数搜索,精度有提高的余地,但随着排班日数的增加,它被认为几乎不可能解决。另一种提高准确性的方法是自己设置QUBO嵌入,但我不知道以我目前的知识做出什么样的嵌入。另外,这样的结果,我觉得很难投入实际使用,因为最多只能安排一个星期的班次。一旦我可以制定至少一个月的时间表,我想进行另一个实验。

其实,量子退火算法在QUBO建模上存在困难的同时,由于量子退火机和伊辛机还没有达到大规模实际问题的全结合bit数,所以,大家也要怀着谨慎的态度看待量子退火的未来。

 

————————————————

本文转载自CSDN博主:gang_unerry

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/weixin_49155137/article/details/131686724

400
0
0
0
关于作者
相关文章
  • 模拟退火算法求解 0-1 背包问题
    背包问题的目标是在给定物品和约束条件下,选择一定的物品,使得它们的总价值最大,同时满足总重 ...
    了解详情 
  • 模拟退火
    1.前言:启发式算法模拟退火算法是一个经典的启发式算法,也被称为智能算法。他们不是数学,而是 ...
    了解详情 
  • 求解包含约束的最优化问题:罚函数法
    外点罚函数法针对包含约束条件的最优化问题,此前介绍的拉格朗日乘子法和KKT条件已经提供一种有 ...
    了解详情 
  • 求解包含约束的最优化问题:拉格朗日乘子法和KKT条件 ...
    无约束梯度类算法中的最速下降法、牛顿法和拟牛顿法,可以直接使用的条件之一为:决策变量都是无 ...
    了解详情 
  • 从“怪异性定理”窥探量子计算的金融应用潜力:算法理性带来的启 ...
    1. 引言:金融科技的量子跃迁金融科技领域一直是新兴技术应用的沃土,不断寻求更高效、更精准的 ...
    了解详情 
在本版发帖返回顶部
快速回复 返回顶部 返回列表
玻色有奖小调研
填写问卷,将免费赠送您5个100bit真机配额
(单选) 您是从哪个渠道得知我们的?*
您是从哪个社交媒体得知我们的?*
您是通过哪个学校的校园宣讲得知我们的呢?
取消

提交成功

真机配额已发放到您的账户,可前往【云平台】查看