2026/10/17 18:27:46

MATLAB量子近似优化算法:组合优化问题求解实现

MATLAB量子近似优化算法:组合优化问题求解实现 简介这份PDF文档围绕组合优化问题中的量子近似优化算法QAOA展开系统讲解如何利用MATLAB实现这一前沿算法。内容从组合优化问题定义、常见类型与传统求解方法讲起逐步深入到量子比特、量子门、哈密顿量构建、量子态演化与参数优化等核心原理并配有MATLAB环境搭建步骤和完整示例代码。文档还以最大割问题为例展示从问题建模到结果分析的全流程同时涵盖算法性能评估与优化策略适合具备基础MATLAB使用经验、希望了解量子优化算法落地实现的工程师和研究者。资源共1个PDF文件大小4.01MB文件结构清晰支持目录跳转与左侧大纲导航方便按章节查阅。文档目前已有65人学习浏览内容完整且图表公式显示正常可作为自学或课题参考的实用资料。1. 组合优化问题与MATLAB量子近似优化算法先讲结论拿到这本《组合优化问题MATLAB量子近似优化算法实现.pdf》先别被量子两个字唬住。我的结论是在MATLAB里实现量子近似优化算法QAOA目的不是去挤量子计算机的算力而是把组合优化问题的目标函数编码成量子系统的哈密顿量用交替演化加经典参数搜索的方式求近似解。它适合两类人一类是想评估这个方向值不值得投入的从业者另一类是课程设计或预研阶段需要一份可跑通原型的开发者。这篇文章就照着这份PDF的核心思路把能复现的路径和踩过的坑一次讲透。2. 把MaxCut翻译成量子哈密顿量组合优化进入量子语言2.1 从目标函数到能量函数为什么组合优化能和量子对得上QAOA能求解组合优化问题关键不在量子纠缠而在一个问题映射绝大多数组合优化目标函数都能写成二次多项式而二次多项式恰好能对应到量子系统哈密顿量在计算基下的对角项。以最经典的MaxCut问题为例给定一张图把所有顶点染成黑白两色希望最大化被切断的边数。对这个目标常见做法是引入变量z_i ∈ {1, -1}把每个顶点的颜色编码成自旋方向那么MaxCut的目标函数可以写成E(z) Σ_{(i,j)∈E} w_ij × (1 - z_i z_j) / 2其中E是边集w_ij是边权重。当z_i和z_j颜色相反时z_i z_j -1这条边贡献w_ij颜色相同时z_i z_j 1贡献0。于是最大化切断边数等价于最大化这个能量函数。把z_i替换成泡利Z算符的期望值就得到一个量子哈密顿量H_C——它在计算基|z下恰好是对角的每个计算基态的能量就是对应的目标函数值。这里有一个选型经验入门QAOA时我一般会建议先用无权重的MaxCut而不是一上来就碰TSP或调度问题。原因是TSP的约束项每个城市访问一次和权重项纠缠在一起映射成Ising模型后需要加很大的惩罚系数调试时很难判断是QAOA没收敛还是惩罚系数设错了。而MaxCut只有纯粹的二元相互作用能量向量构造简单先把这条路走通再往带约束的问题扩展心理负担小很多。把这个能量函数升级成一个量子算符后问题就变成了能不能让量子态尽量集中到低能态上也就是让测量结果大概率落在高目标函数值的切割方案上。这正是QAOA要做的事。理解这一点后面所有代码都是在为这个目标服务。2.2 量子近似优化算法的机制两个算符交替演化加一个经典优化器QAOA的结构比想象中简单。它不模拟量子退火的物理过程而是构造一个参数化的量子电路然后让经典优化器去调参数。这个电路只有两类操作。第一类是代价层演化U_C(γ) exp(-iγH_C)作用是让量子态在代价哈密顿量下演化演化时间γ是参数第二类是混合层演化U_M(β) exp(-iβH_M)其中H_M是所有单比特X算符之和作用是让量子态在不同计算基之间混合不给搜索陷入某个局部最优的机会。整个算法流程是从所有计算基态的均匀叠加态出发交替施加代价层和混合层重复p轮最后测量代价哈密顿量的期望值。p就是QAOA的层数也叫电路深度。p越大参数数量越多γ和β各有p个理论上能逼近的最优解质量越高。测量得到的期望值作为目标函数交给经典优化器去调整这2p个参数反复迭代直到收敛。所以QAOA的本质不是纯量子算法而是一个量子和经典协同的变分算法量子电路负责生成候选分布经典优化器负责找最好的电路参数。那为什么在MATLAB里做这件事比在Python环境里更顺手这个问题我在做第一版模拟器时体会得很深。MATLAB的矩阵代数语法和这套算法的论文伪代码几乎一一对应稀疏矩阵、kron、逐元素运算都是内置能力不需要额外引入任何量子计算库。更重要的是MATLAB自带fminunc、patternsearch这类优化工具箱QAOA最烧时间的参数搜索部分可以直接复用不用自己写优化器。对一个算法验证型项目来说这意味着从读论文到跑出第一组结果的时间被压缩到很短。顺带说一句这份PDF里讲的实现是经典模拟器不是上真实量子硬件。真实硬件上有噪声、有退相干还有测量误差模拟器则是在MATLAB里用矩阵和复数完整演算量子态。模拟器的局限是态空间随比特数指数膨胀n20比特就需要约一百万个复数存储它的价值是把算法逻辑彻底搞清楚是在真机上调试前必经的一步。这一章把原理和选型理由立住了下一章进入真正能复现的代码。3. 在MATLAB里手搓QAOA最小可跑通的实现与参数说明3.1 组合问题转能量向量不显式构造哈密顿量矩阵很多第一次实现QAOA的人会直接去构造2^n×2^n的哈密顿量矩阵然后调用expm算矩阵指数。这个做法在n8以下还能跑n10以上直接翻车——16384×16384的稠密矩阵存储加指数运算MATLAB会卡到怀疑人生。常见做法是把H_C当成对角算符处理只保留一个长度为2^n的能量向量因为H_C在计算基下天然是对角的。下面这段代码从一个边列表构建能量向量E每个分量对应一个计算基态的能量。function E build_energy_vector(nEdges, nBits) % nBits : 顶点数即量子比特数 % nEdges : 边列表每一行是 [i, j, w]i、j从1开始 N 2^nBits; idx (0:N-1); % 计算基态的十进制索引采用LSB位序 bits bitget(idx, nBits:-1:1); % 把每个索引拆成比特矩阵大小为 N x nBits z 1 - 2*bits; % 比特0/1映射到自旋1/-1 E zeros(N, 1); for k 1:size(nEdges,1) i nEdges(k,1); j nEdges(k,2); w nEdges(k,3); E E w * (1 - z(:,i) .* z(:,j)) / 2; end end逻辑说明bitget(idx, nBits:-1:1)把十进制索引拆成二进制位这里位序是从最高位到最低位也就是把索引的高位对应到顶点1。z矩阵把0/1映射为自旋1/-1这是MaxCut目标函数的标准Ising变换。循环累加每条边的能量贡献最终E(k)就是第k个计算基态对应的目标函数值。参数说明nBits是图顶点数也就是需要的量子比特数。边列表nEdges建议用double矩阵保存权重w取正数。如果不带权重就把所有w设为1。这段代码跑完后你会发现E的很多分量相同因为不同切割方案可能有相同的目标函数值这很正常。验证E是否正确有一个笨办法随机挑几个切割方案手算目标函数值和E(index)对一下。这一步花费一分钟能省掉后续一小时的玄学排错。3.2 代价层与混合层用相位旋转和比特级旋转实现演化代价层exp(-iγH_C)因为H_C对角可以直接对态向量的每个分量做相位旋转不需要任何矩阵指数运算。混合层exp(-iβH_M)稍微复杂一点它是每个比特上单比特旋转的复合常见做法是逐比特应用2×2旋转矩阵。我习惯把单比特旋转算子封装成一个函数便于在p层循环里反复调用。function psi apply_cost_layer(psi, gamma, E) % 代价层对角相位旋转 psi psi .* exp(-1i * gamma * E); end function psi apply_mixer_layer(psi, beta, nBits) % 混合层对每个比特应用 exp(-i*beta*X) 旋转 % X [0 1; 1 0]因此 U exp(-i*beta*X) [cos(beta), -1i*sin(beta); -1i*sin(beta), cos(beta)] for k 1:nBits dims [2^k 2 2^(nBits-k)]; psi reshape(psi, dims); % 把目标比特拆到中间维 psi permute(psi, [2 1 3]); % 目标比特移到第一维 a psi(1,:); b psi(2,:); new_a cos(beta) * a - 1i * sin(beta) * b; new_b -1i * sin(beta) * a cos(beta) * b; psi [new_a; new_b]; psi permute(psi, [2 1 3]); % 还原维序 psi reshape(psi, [], 1); end end逻辑说明apply_cost_layer把E向量作为相位基准gamma控制旋转角度这一层让高能量的态获得不同相位为后续干涉做准备。apply_mixer_layer的核心技巧是reshape和permute把第k个比特拆到矩阵的中间维度然后把目标比特提到第一维使得单比特旋转可以一次作用于所有相关分量。new_a和new_b的变换式就是2×2旋转矩阵exp(-iβX)与态向量的乘法结果。参数说明这里有个初始值经验——beta的常用取值范围是(0, π/2)gamma是(0, π)。如果beta取太大混合层会反复翻转比特导致收敛变慢如果gamma全取小角度代价层的相位影响微弱期望值变化很小优化器会表现成目标函数平台期很长。我一般第一轮初始化用gamma均匀分布在(0, π)内、beta均匀分布在(0, π/4)内。这个区间不是理论最优但配合后面要讲的优化器能显著减少翻车概率。3.3 目标函数与最小可跑通循环把量子电路的期望值送给优化器有了两个演化层接下来组装QAOA整体。需要定义目标函数给定一组角度gamma向量p个、beta向量p个从均匀叠加态出发交替演化最后计算代价哈密顿量的期望值。由于MATLAB优化器默认求最小值目标函数返回期望值的相反数。下面是完整的目标函数与调用脚本。function fval qaoa_objective(angles, nBits, nEdges, E) p numel(angles) / 2; gamma angles(1:p); beta angles(p1:end); N 2^nBits; % 初始态所有计算基态的等幅叠加 psi (1/sqrt(N)) * ones(N, 1); for layer 1:p psi apply_cost_layer(psi, gamma(layer), E); psi apply_mixer_layer(psi, beta(layer), nBits); end % 期望值 psi|H_C|psi由于 H_C 对角直接用概率幅计算 prob abs(psi).^2; expVal sum(prob .* E); fval -expVal; % 优化器求最小取反 end % 主脚本跑通调用 nBits 8; nEdges [1 2 1; 2 3 1; 3 4 1; 4 1 1; 1 5 1; 2 6 1; 3 7 1; 4 8 1; 5 6 1; 6 7 1; 7 8 1; 5 8 1]; E build_energy_vector(nEdges, nBits); p 2; x0 [rand(p,1)*pi, rand(p,1)*pi/4]; % 随机初始化gamma在(0,pi)、beta在(0,pi/4) options optimoptions(fminunc, Display, iter, MaxIterations, 200); [xOpt, fMin] fminunc((x) qaoa_objective(x, nBits, nEdges, E), x0, options);逻辑说明qaoa_objective先从角度向量里拆出gamma和betap是层数。初始态用ones向量除以sqrt(N)构造均匀叠加。每层先做代价演化再做混合演化顺序不能反——先让系统感受到代价方向再混合探索这是QAOA的标准电路结构。期望值用概率幅平方乘以能量再求和因为H_C对角这一步不需要矩阵乘法。fminunc输出xOpt就是要找的最优角度fMin是最小负期望值它的绝对值就是找到的最大目标函数值。参数说明MaxIterations设为200是给优化器一个预算模拟器规模小的时候通常不到100步就收敛。x0的随机初始化意味着每次结果可能不同这一点很重要——QAOA目标函数有多峰单次优化容易停在局部最优。经验值是多跑5次初值取最优结果。fminunc默认使用BFGS类算法需要目标函数平滑我们的目标函数是三角函数的组合平滑性没问题。如果你手里的MATLAB版本较旧把Display参数去掉或改为final即可。4. QAOA参数调优与避坑排查从翻车现场倒推的四个问题4.1 层数p、初始化与优化器三个最关键的参数决策层数p是QAOA里最能体现投入产出比的参数。p1时只有一个代价层和一个混合层参数只有两个优化极快但理论上有性能上限对于MaxCut问题在部分图上只能达到约0.69的近似比p2性能明显改善参数变成4个p5以上在模拟器中通常已经能逼近最优解但每加一层代价层的相位旋转和混合层的比特旋转都要多算一轮目标函数的非凸性也更强优化器更容易陷入局部极值。我的经验是n不超过12时p从2开始试达不到理想近似比再加到4。不要一上来就p8优化器会痛苦你也会痛苦。初始化和优化器是配套决策。fminunc这种基于梯度的优化器收敛快但对初值敏感patternsearch不依赖梯度适合在初始阶段做全局搜索。我一般的流程是先用patternsearch跑一轮较粗的搜索把结果作为初值喂给fminunc精调。这个组合叫两步优化法效果比单用任何一个都好。如果你对MATLAB的global optimization工具箱不熟也可以用fminunc多随机初值跑几次取最优结果代价是耗时增加。还有一个经常被忽略的参数目标函数里的数值尺度。期望值的大小依赖图的边数和权重如果一张图有几百条边期望值可能在几十的量级如果只有几条边期望值可能只有几这会影响优化器的步长选择。常见的做法是调整fminunc的OptimalityTolerance和StepTolerance或者更简单地把期望值除以边数归一化。我在实现时会把目标函数写成负近似比而不是负期望值这样优化器面对的量纲更稳定。4.2 避坑排查清单现象、原因与解决方案坑一n10时运行直接Out of Memory。这是最常见的新手翻车点。原因是很多人听了「量子模拟」就直觉地去构造2^n×2^n的完整哈密顿量矩阵再算expmn10时矩阵是1024×1024的稠密复数矩阵存储尚可但expm运算会生成多个临时满阵内存峰值翻好几倍。解决方式就是第3章的做法H_C保持对角向量形式混合层逐比特应用全程不构造也不指数化任何满矩阵。这样n16时态向量长度65536内存占用也就几兆字节。坑二目标函数在优化过程中长时间不变优化器一步步慢悠悠往下爬。原因是代价层角度gamma初始值全取在接近0的位置相位旋转角度太小态向量几乎没有变化或者beta取太大混合旋转把态反复搅动期望值在平均值附近震荡。解决方式是把gamma初始化范围从(0, π)缩到(0, π/2)beta初始化范围缩到(0, π/4)然后用patternsearch做一轮粗搜。还有一个细节每次调用目标函数时随机初始化会导致优化器每次爬山的方向都变看起来不收敛实际是初值不一致。把初始化移到主脚本里目标函数内部不要碰rand。坑三优化器收敛了但最后切割方案的边数还不如贪心算法。这个现象我在做某调度问题的预研时遇到过原因是目标函数的符号搞反了。MaxCut的目标是最大化切断边数而优化器求最小所以目标函数是负期望值。如果你在构造E时把权重符号写反比如用w * (1 z_i * z_j)的形式那么能量最低的态反而对应最差的切割方案。解决方式是一开始就写一个穷举验证脚本对n10的随机图把所有2^n个切割方案的目标函数值算出来同时对QAOA最终采样的解做对比如果最优值对不上先查符号再查位序。坑四位序混淆导致采样结果看起来完全随机。MATLAB的bitget默认从最高位拆起而很多资料里的量子态索引用的是最低位在前两者混用会让「第i个顶点」和「态索引的第i位」对不上号。现象是能量向量E没问题但QAOA采样的解映射回顶点颜色时一片混乱。解决方式是统一约定代码里所有bitget和bitget的位序都用nBits:-1:1也就是最高位对应第一个顶点采样解码时同样保持这个顺序。这个坑花不了几分钟标注清楚但一旦踩进去排查时间以小时计。5. 从MaxCut走向工程实战验证结果的三个技巧与一句结论5.1 用近似比和穷举对照判断结果不是玄学量子算法最大的信任危机是结果像玄学。我的习惯是把最终评价指标定为近似比QAOA找到的目标函数值除以精确最优值。精确最优值来自穷举n16时很快或分支定界。近似比在0.9以上说明这个实例上QAOA确实逼近了最优如果只有0.7以下问题可能出在层数不足或初始化不当而不是算法本身不适合。5.2 规模扩展的内存边界与稀疏化经验模拟器能跑到的上限大约在n18到n20再往上态向量长度超过26万虽然内存勉强够但每次目标函数调用要遍历所有分量优化迭代会慢到无法接受。常见做法是把图限制在稀疏图边数远小于n(n-1)/2这样E向量的计算仍然很快。如果确实需要更大规模方向是把目标函数改成QUBO形式并用稀疏矩阵存储权重但这是另一个层面的优化建议等n16跑顺后再碰。5.3 这个方向值不值得做我的判断回到标题本身这份PDF讲的是组合优化问题在MATLAB环境下的量子近似优化算法实现它最大的价值是一套可以跑的算法骨架——从能量向量构造到两层演化再到优化器对接代码量不到一百行却完整覆盖了QAOA的全部要素。我现在的普遍做法是把它当成原型验证工具拿到一个新的组合优化问题时先用QAOA模拟器跑一遍看近似比再决定值不值得投入更多资源。有一次用它验证某个任务分配问题的简化模型QAOA p4的近似比做到0.95以上信心一下就建立起来了。如果你正卡在「不了解量子算法怎么落地」的僵局里从这套实现入手是最划算的路径希望帮到你。本文还有配套的精品资源点击获取