QAOA 量子近似优化算法¶
1. 算法简介¶
1.1 什么是 QAOA?¶
QAOA (Quantum Approximate Optimization Algorithm) 是一种用于解决组合优化问题的变分量子算法。
核心特点:
- 基于量子绝热定理
- 使用参数化量子电路
- 结合量子计算和经典优化
- 适用于 NQ-hard 问题
1.2 应用场景¶
QAOA 可以应用于多种组合优化问题:
- 最大割问题 (MaxCut): 将图的顶点分成两组,使得连接两组的边数最多
- 图着色问题: 用最少的颜色给图的顶点着色,使相邻顶点颜色不同
- 旅行商问题 (TSP): 找到访问所有城市的最短路径
- 背包问题: 在容量限制下选择物品使价值最大化
- 3-SAT 问题: 布尔可满足性问题
1.3 难度等级¶
⭐⭐⭐ 中级 (需要量子计算基础知识和优化理论理解)
2. 理论基础¶
2.1 QAOA 原理¶
QAOA 基于量子绝热定理的离散化版本。其核心思想是:
- 绝热演化: 从易制备的基态出发,缓慢演化到目标哈密顿量的基态
- Trotter 分解: 将连续的绝热演化离散化为多步量子门操作
- 变分优化: 通过优化参数来近似最优解
2.2 QAOA 电路结构¶
QAOA 电路由以下部分组成:
初始态: |+⟩^⊗n (所有量子比特的叠加态)
↓
重复 p 层:
1. 问题哈密顿量 U_C(γ) = exp(-iγH_C)
2. 混合哈密顿量 U_B(β) = exp(-iβH_B)
↓
测量: 在 Z 基测量所有量子比特
2.3 哈密顿量¶
问题哈密顿量 (Cost Hamiltonian)¶
编码要解决的优化问题。对于最大割问题:
$$H_C = \frac{1}{2}\sum_{(i,j) \in E} w_{ij}(1 - Z_i Z_j)$$
其中 $w_{ij}$ 是边 $(i,j)$ 的权重。
混合哈密顿量 (Mixer Hamiltonian)¶
用于在解空间中探索:
$$H_B = \sum_{i=1}^n X_i$$
其中 $X_i$ 是作用在第 $i$ 个量子比特上的 Pauli-X 门。
2.4 参数优化¶
QAOA 的性能依赖于参数 $\vec{\gamma}, \vec{\beta}$ 的选择。优化过程:
- 初始化参数 $\vec{\gamma}^{(0)}, \vec{\beta}^{(0)}$
- 制备参数化态 $|\psi(\vec{\gamma}, \vec{\beta})\rangle$
- 测量目标函数 $\langle H_C \rangle$
- 使用经典优化器更新参数
- 重复 2-4 直到收敛
常用优化器:
- Adam (自适应学习率)
- RMSprop (均方根传播)
- COBYLA (无梯度优化)
- SPSA (同时扰动随机逼近)
3. 实现部分¶
3.1 导入必要的库¶
In [ ]:
Copied!
import deepquantum as dq
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import numpy as np
from collections import defaultdict
# 设置随机种子以保证结果可复现
torch.manual_seed(42)
np.random.seed(42)
import deepquantum as dq
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import numpy as np
from collections import defaultdict
# 设置随机种子以保证结果可复现
torch.manual_seed(42)
np.random.seed(42)
3.2 定义 QAOA 类¶
我们创建一个 QAOA 类来实现量子近似优化算法:
In [ ]:
Copied!
class QAOA(nn.Module):
"""
QAOA (Quantum Approximate Optimization Algorithm) 实现
参数:
nqubit: 量子比特数量
pairs: 相互作用的量子比特对列表
coefs: 每个相互作用的权重
step: QAOA 层数 (p)
"""
def __init__(self, nqubit, pairs, coefs, step):
super().__init__()
self.nqubit = nqubit
self.pairs = pairs
self.coefs = torch.tensor(coefs)
self.step = step
# 定义可训练参数:gamma 和 beta
# gamma: 问题哈密顿量的旋转角度
# beta: 混合哈密顿量的旋转角度
self.gamma = nn.Parameter(0.1 * torch.ones(step))
self.beta = nn.Parameter(torch.ones(step))
# 构建量子电路
self.cir = dq.QubitCircuit(nqubit)
# 初始化:将所有量子比特置于叠加态
self.cir.hlayer()
self.cir.barrier()
# 构建 p 层 QAOA 电路
for _ in range(step):
# 问题哈密顿量 U_C(gamma)
for wires in pairs:
# 对于每条边,应用 CNOT-RZ-CNOT 实现 ZZ 相互作用
self.cir.cnot(wires[0], wires[1])
self.cir.rz(wires[1], encode=True) # 编码模式,稍后注入参数
self.cir.cnot(wires[0], wires[1])
self.cir.barrier()
# 混合哈密顿量 U_B(beta)
for i in range(nqubit):
self.cir.rx(i, encode=True) # RX 旋转,稍后注入参数
self.cir.barrier()
# 设置可观测量的测量
for wires in pairs:
self.cir.observable(wires)
@property
def params(self):
"""构建完整的参数向量"""
params = torch.empty(0)
for i in range(self.step):
# 计算第 i 层的参数
gammas = 2 * self.gamma[i] * self.coefs
betas = 2 * self.beta[i].repeat(self.nqubit)
params = torch.cat([params, gammas, betas])
return params
def draw(self):
"""绘制量子电路图"""
self.cir.encode(self.params)
return self.cir.draw()
def measure(self):
"""测量量子电路,返回测量结果统计"""
return self.cir.measure()
def forward(self):
"""
前向传播:计算期望值(目标函数)
返回:
期望值(越小越好,因为是负的最大割值)
"""
self.cir(self.params)
return sum(self.coefs * self.cir.expectation())
class QAOA(nn.Module):
"""
QAOA (Quantum Approximate Optimization Algorithm) 实现
参数:
nqubit: 量子比特数量
pairs: 相互作用的量子比特对列表
coefs: 每个相互作用的权重
step: QAOA 层数 (p)
"""
def __init__(self, nqubit, pairs, coefs, step):
super().__init__()
self.nqubit = nqubit
self.pairs = pairs
self.coefs = torch.tensor(coefs)
self.step = step
# 定义可训练参数:gamma 和 beta
# gamma: 问题哈密顿量的旋转角度
# beta: 混合哈密顿量的旋转角度
self.gamma = nn.Parameter(0.1 * torch.ones(step))
self.beta = nn.Parameter(torch.ones(step))
# 构建量子电路
self.cir = dq.QubitCircuit(nqubit)
# 初始化:将所有量子比特置于叠加态
self.cir.hlayer()
self.cir.barrier()
# 构建 p 层 QAOA 电路
for _ in range(step):
# 问题哈密顿量 U_C(gamma)
for wires in pairs:
# 对于每条边,应用 CNOT-RZ-CNOT 实现 ZZ 相互作用
self.cir.cnot(wires[0], wires[1])
self.cir.rz(wires[1], encode=True) # 编码模式,稍后注入参数
self.cir.cnot(wires[0], wires[1])
self.cir.barrier()
# 混合哈密顿量 U_B(beta)
for i in range(nqubit):
self.cir.rx(i, encode=True) # RX 旋转,稍后注入参数
self.cir.barrier()
# 设置可观测量的测量
for wires in pairs:
self.cir.observable(wires)
@property
def params(self):
"""构建完整的参数向量"""
params = torch.empty(0)
for i in range(self.step):
# 计算第 i 层的参数
gammas = 2 * self.gamma[i] * self.coefs
betas = 2 * self.beta[i].repeat(self.nqubit)
params = torch.cat([params, gammas, betas])
return params
def draw(self):
"""绘制量子电路图"""
self.cir.encode(self.params)
return self.cir.draw()
def measure(self):
"""测量量子电路,返回测量结果统计"""
return self.cir.measure()
def forward(self):
"""
前向传播:计算期望值(目标函数)
返回:
期望值(越小越好,因为是负的最大割值)
"""
self.cir(self.params)
return sum(self.coefs * self.cir.expectation())
3.3 定义训练函数¶
In [ ]:
Copied!
def trainer(model, epoch, lr, optimizer_type='Adam'):
"""
训练 QAOA 模型
参数:
model: QAOA 模型
epoch: 训练轮数
lr: 学习率
optimizer_type: 优化器类型 ('Adam', 'RMSprop')
返回:
trained_model: 训练好的模型
metrics: 训练过程的损失记录
"""
# 选择优化器
if optimizer_type == 'Adam':
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
elif optimizer_type == 'RMSprop':
optimizer = torch.optim.RMSprop(
model.parameters(), lr, alpha=0.99, eps=1e-08,
weight_decay=0, momentum=0, centered=False
)
else:
raise ValueError(f"未知的优化器类型: {optimizer_type}")
train_loss_list = []
print(f"开始训练 (优化器: {optimizer_type}, 学习率: {lr})")
print("-" * 60)
for e in range(epoch):
# 前向传播
y_pred = model()
loss = y_pred # 损失函数就是期望值
# 反向传播和参数更新
optimizer.zero_grad()
loss.backward(retain_graph=True)
optimizer.step()
# 记录损失
train_loss_list.append(loss.detach().item())
# 打印进度
if (e + 1) % 10 == 0 or e == 0:
print(f'轮次: {e+1:3d}/{epoch}, 损失: {loss.item():.6f}')
print("-" * 60)
print("训练完成!")
metrics = {
'epoch': list(range(1, len(train_loss_list) + 1)),
'train_loss': train_loss_list
}
return model, metrics
def trainer(model, epoch, lr, optimizer_type='Adam'):
"""
训练 QAOA 模型
参数:
model: QAOA 模型
epoch: 训练轮数
lr: 学习率
optimizer_type: 优化器类型 ('Adam', 'RMSprop')
返回:
trained_model: 训练好的模型
metrics: 训练过程的损失记录
"""
# 选择优化器
if optimizer_type == 'Adam':
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
elif optimizer_type == 'RMSprop':
optimizer = torch.optim.RMSprop(
model.parameters(), lr, alpha=0.99, eps=1e-08,
weight_decay=0, momentum=0, centered=False
)
else:
raise ValueError(f"未知的优化器类型: {optimizer_type}")
train_loss_list = []
print(f"开始训练 (优化器: {optimizer_type}, 学习率: {lr})")
print("-" * 60)
for e in range(epoch):
# 前向传播
y_pred = model()
loss = y_pred # 损失函数就是期望值
# 反向传播和参数更新
optimizer.zero_grad()
loss.backward(retain_graph=True)
optimizer.step()
# 记录损失
train_loss_list.append(loss.detach().item())
# 打印进度
if (e + 1) % 10 == 0 or e == 0:
print(f'轮次: {e+1:3d}/{epoch}, 损失: {loss.item():.6f}')
print("-" * 60)
print("训练完成!")
metrics = {
'epoch': list(range(1, len(train_loss_list) + 1)),
'train_loss': train_loss_list
}
return model, metrics
In [ ]:
Copied!
# 定义问题:每条边上的数字表示两个节点之间的矛盾值
# 目标:将节点分成两组,使得跨组的边权重之和最大
problem = {
'Z0 Z4': 0.73, 'Z0 Z5': 0.33, 'Z0 Z6': 0.50,
'Z1 Z4': 0.69, 'Z1 Z5': 0.36,
'Z2 Z5': 0.88, 'Z2 Z6': 0.58,
'Z3 Z5': 0.67, 'Z3 Z6': 0.43
}
# 解析问题,提取量子比特对和权重
pairs = []
coefs = []
for key, value in problem.items():
temp = []
for item in key.split():
if item[0] == 'Z':
temp.append(int(item[1:]))
if len(temp) == 2:
pairs.append(temp)
coefs.append(value)
print('量子比特对:', pairs)
print('权重:', coefs)
# 问题参数
nqubit = 7 # 量子比特总数
step = 4 # QAOA 层数 (层数越高,理论上精度越高,但训练更困难)
lr = 0.01 # 学习率
epoch = 100 # 训练轮数
print(f"\n问题规模: {nqubit} 个量子比特, {len(pairs)} 条边")
print(f"QAOA 层数: {step}")
# 定义问题:每条边上的数字表示两个节点之间的矛盾值
# 目标:将节点分成两组,使得跨组的边权重之和最大
problem = {
'Z0 Z4': 0.73, 'Z0 Z5': 0.33, 'Z0 Z6': 0.50,
'Z1 Z4': 0.69, 'Z1 Z5': 0.36,
'Z2 Z5': 0.88, 'Z2 Z6': 0.58,
'Z3 Z5': 0.67, 'Z3 Z6': 0.43
}
# 解析问题,提取量子比特对和权重
pairs = []
coefs = []
for key, value in problem.items():
temp = []
for item in key.split():
if item[0] == 'Z':
temp.append(int(item[1:]))
if len(temp) == 2:
pairs.append(temp)
coefs.append(value)
print('量子比特对:', pairs)
print('权重:', coefs)
# 问题参数
nqubit = 7 # 量子比特总数
step = 4 # QAOA 层数 (层数越高,理论上精度越高,但训练更困难)
lr = 0.01 # 学习率
epoch = 100 # 训练轮数
print(f"\n问题规模: {nqubit} 个量子比特, {len(pairs)} 条边")
print(f"QAOA 层数: {step}")
3.5 创建并训练模型¶
In [ ]:
Copied!
# 创建 QAOA 模型
qaoa_model = QAOA(nqubit, pairs, coefs, step)
print("模型结构:")
print(f" - 量子比特数: {nqubit}")
print(f" - QAOA 层数: {step}")
print(f" - 可训练参数: {2 * step} 个 (gamma: {step} 个, beta: {step} 个)")
print(f" - 总参数量: {sum(p.numel() for p in qaoa_model.parameters())}")
# 创建 QAOA 模型
qaoa_model = QAOA(nqubit, pairs, coefs, step)
print("模型结构:")
print(f" - 量子比特数: {nqubit}")
print(f" - QAOA 层数: {step}")
print(f" - 可训练参数: {2 * step} 个 (gamma: {step} 个, beta: {step} 个)")
print(f" - 总参数量: {sum(p.numel() for p in qaoa_model.parameters())}")
In [ ]:
Copied!
# 训练模型
trained_model, metrics = trainer(qaoa_model, epoch, lr, optimizer_type='Adam')
# 训练模型
trained_model, metrics = trainer(qaoa_model, epoch, lr, optimizer_type='Adam')
3.6 可视化训练过程¶
In [ ]:
Copied!
# 绘制训练损失曲线
plt.figure(figsize=(10, 6))
plt.plot(metrics['epoch'], metrics['train_loss'], 'b-', linewidth=2, label='训练损失')
plt.xlabel('训练轮次', fontsize=12)
plt.ylabel('损失 (目标函数值)', fontsize=12)
plt.title('QAOA 训练过程', fontsize=14, fontweight='bold')
plt.grid(True, alpha=0.3)
plt.legend(fontsize=11)
plt.tight_layout()
plt.show()
# 输出训练统计信息
final_loss = metrics['train_loss'][-1]
best_loss = min(metrics['train_loss'])
improvement = metrics['train_loss'][0] - final_loss
print("\n训练统计:")
print(f" 初始损失: {metrics['train_loss'][0]:.6f}")
print(f" 最终损失: {final_loss:.6f}")
print(f" 最佳损失: {best_loss:.6f}")
print(f" 改进幅度: {improvement:.6f}")
# 绘制训练损失曲线
plt.figure(figsize=(10, 6))
plt.plot(metrics['epoch'], metrics['train_loss'], 'b-', linewidth=2, label='训练损失')
plt.xlabel('训练轮次', fontsize=12)
plt.ylabel('损失 (目标函数值)', fontsize=12)
plt.title('QAOA 训练过程', fontsize=14, fontweight='bold')
plt.grid(True, alpha=0.3)
plt.legend(fontsize=11)
plt.tight_layout()
plt.show()
# 输出训练统计信息
final_loss = metrics['train_loss'][-1]
best_loss = min(metrics['train_loss'])
improvement = metrics['train_loss'][0] - final_loss
print("\n训练统计:")
print(f" 初始损失: {metrics['train_loss'][0]:.6f}")
print(f" 最终损失: {final_loss:.6f}")
print(f" 最佳损失: {best_loss:.6f}")
print(f" 改进幅度: {improvement:.6f}")
3.7 测量和分析结果¶
In [ ]:
Copied!
# 执行测量
measurement_results = trained_model.measure()
print("\n测量结果 (前20个):")
print("-" * 60)
for i, (state, count) in enumerate(list(measurement_results.items())[:20]):
percentage = count / sum(measurement_results.values()) * 100
print(f" 状态 {state}: {count:4d} 次 ({percentage:5.2f}%)")
# 找到最优解
max_count = max(measurement_results.values())
best_states = [state for state, count in measurement_results.items()
if count == max_count]
print("\n" + "=" * 60)
print("最优解:")
print("=" * 60)
for state in best_states:
print(f" 最佳分割方案: {state}")
print(f" 出现次数: {max_count}")
# 解析分割方案
group0 = [i for i, bit in enumerate(state[::-1]) if bit == '0']
group1 = [i for i, bit in enumerate(state[::-1]) if bit == '1']
print(f" 组0: {group0}")
print(f" 组1: {group1}")
# 计算目标函数值
obj_value = 0
for (i, j), coef in zip(pairs, coefs):
if state[::-1][i] != state[::-1][j]: # 跨组
obj_value += coef
print(f" 目标函数值: {obj_value:.6f}")
# 执行测量
measurement_results = trained_model.measure()
print("\n测量结果 (前20个):")
print("-" * 60)
for i, (state, count) in enumerate(list(measurement_results.items())[:20]):
percentage = count / sum(measurement_results.values()) * 100
print(f" 状态 {state}: {count:4d} 次 ({percentage:5.2f}%)")
# 找到最优解
max_count = max(measurement_results.values())
best_states = [state for state, count in measurement_results.items()
if count == max_count]
print("\n" + "=" * 60)
print("最优解:")
print("=" * 60)
for state in best_states:
print(f" 最佳分割方案: {state}")
print(f" 出现次数: {max_count}")
# 解析分割方案
group0 = [i for i, bit in enumerate(state[::-1]) if bit == '0']
group1 = [i for i, bit in enumerate(state[::-1]) if bit == '1']
print(f" 组0: {group0}")
print(f" 组1: {group1}")
# 计算目标函数值
obj_value = 0
for (i, j), coef in zip(pairs, coefs):
if state[::-1][i] != state[::-1][j]: # 跨组
obj_value += coef
print(f" 目标函数值: {obj_value:.6f}")
3.8 绘制量子电路¶
In [ ]:
Copied!
# 绘制训练后的量子电路
print("\n量子电路结构:")
print("-" * 60)
circuit_diagram = trained_model.draw()
print(circuit_diagram)
# 绘制训练后的量子电路
print("\n量子电路结构:")
print("-" * 60)
circuit_diagram = trained_model.draw()
print(circuit_diagram)
3.9 结果可视化¶
In [ ]:
Copied!
# 统计测量结果
sorted_results = sorted(measurement_results.items(),
key=lambda x: x[1], reverse=True)
states = [item[0] for item in sorted_results[:15]]
counts = [item[1] for item in sorted_results[:15]]
# 绘制测量结果柱状图
plt.figure(figsize=(14, 6))
bars = plt.bar(range(len(states)), counts, color='steelblue', alpha=0.7)
plt.xlabel('量子态', fontsize=12)
plt.ylabel('测量次数', fontsize=12)
plt.title('QAOA 测量结果分布 (前15个)', fontsize=14, fontweight='bold')
plt.xticks(range(len(states)), states, rotation=45)
plt.grid(axis='y', alpha=0.3)
# 标记最优解
for i, (state, count) in enumerate(zip(states, counts)):
if state in best_states:
bars[i].set_color('red')
bars[i].set_alpha(0.8)
plt.text(i, count, ' 最优', ha='center', va='bottom', fontsize=10)
plt.tight_layout()
plt.show()
# 绘制饼图
plt.figure(figsize=(10, 8))
top_states = states[:10]
top_counts = counts[:10]
other_count = sum(measurement_results.values()) - sum(top_counts)
labels = top_states + ['其他']
sizes = top_counts + [other_count]
colors = plt.cm.Set3(range(len(labels)))
plt.pie(sizes, labels=labels, autopct='%1.1f%%', colors=colors, startangle=90)
plt.title('测量结果分布 (饼图)', fontsize=14, fontweight='bold')
plt.axis('equal')
plt.tight_layout()
plt.show()
# 统计测量结果
sorted_results = sorted(measurement_results.items(),
key=lambda x: x[1], reverse=True)
states = [item[0] for item in sorted_results[:15]]
counts = [item[1] for item in sorted_results[:15]]
# 绘制测量结果柱状图
plt.figure(figsize=(14, 6))
bars = plt.bar(range(len(states)), counts, color='steelblue', alpha=0.7)
plt.xlabel('量子态', fontsize=12)
plt.ylabel('测量次数', fontsize=12)
plt.title('QAOA 测量结果分布 (前15个)', fontsize=14, fontweight='bold')
plt.xticks(range(len(states)), states, rotation=45)
plt.grid(axis='y', alpha=0.3)
# 标记最优解
for i, (state, count) in enumerate(zip(states, counts)):
if state in best_states:
bars[i].set_color('red')
bars[i].set_alpha(0.8)
plt.text(i, count, ' 最优', ha='center', va='bottom', fontsize=10)
plt.tight_layout()
plt.show()
# 绘制饼图
plt.figure(figsize=(10, 8))
top_states = states[:10]
top_counts = counts[:10]
other_count = sum(measurement_results.values()) - sum(top_counts)
labels = top_states + ['其他']
sizes = top_counts + [other_count]
colors = plt.cm.Set3(range(len(labels)))
plt.pie(sizes, labels=labels, autopct='%1.1f%%', colors=colors, startangle=90)
plt.title('测量结果分布 (饼图)', fontsize=14, fontweight='bold')
plt.axis('equal')
plt.tight_layout()
plt.show()
4. 算法分析与讨论¶
4.1 QAOA 的优势¶
- 理论保证: 基于量子绝热定理,有理论基础
- 灵活性: 可以应用于各种组合优化问题
- 可扩展: 通过增加层数 p 提高精度
- 混合架构: 结合量子计算和经典优化
4.2 QAOA 的挑战¶
- 优化困难: 参数优化可能陷入局部最优
- 噪声敏感: 当前量子设备的噪声影响性能
- 资源需求: 需要较深的量子电路
- 经典模拟: 经典模拟的资源消耗随量子比特数指数增长
4.3 实用建议¶
参数初始化¶
# 不同的初始化策略
gamma = 0.1 * torch.ones(step) # 小值初始化
beta = torch.ones(step) # 单位初始化
# 或使用随机初始化
gamma = torch.rand(step) * 0.1
beta = torch.rand(step) * np.pi
层数选择¶
- p=1: 快速但精度较低,适合初步探索
- p=2-3: 平衡性能和计算成本
- p≥4: 更高精度,但优化困难
优化器选择¶
- Adam: 适合大多数情况,收敛稳定
- RMSprop: 适合非平稳目标
- COBYLA: 无梯度优化,适合真实量子设备
5. 进阶应用¶
5.1 最大割问题的不同实例¶
In [ ]:
Copied!
# 示例:一个简单的 4 节点图
def solve_maxcut_example(problem_dict, nqubits, step=2, epochs=50, lr=0.01):
"""
通用的 MaxCut 求解函数
参数:
problem_dict: 问题字典 {'Zi Zj': weight, ...}
nqubits: 量子比特数
step: QAOA 层数
epochs: 训练轮数
lr: 学习率
返回:
trained_model: 训练好的模型
results: 测量结果
"""
# 解析问题
pairs = []
coefs = []
for key, value in problem_dict.items():
temp = []
for item in key.split():
if item[0] == 'Z':
temp.append(int(item[1:]))
if len(temp) == 2:
pairs.append(temp)
coefs.append(value)
# 创建并训练模型
model = QAOA(nqubits, pairs, coefs, step)
trained_model, _ = trainer(model, epochs, lr)
# 测量结果
results = trained_model.measure()
return trained_model, results
# 示例:正方形图 (4个节点,4条边)
square_problem = {
'Z0 Z1': 1.0, 'Z1 Z2': 1.0,
'Z2 Z3': 1.0, 'Z3 Z0': 1.0
}
print("示例:正方形图的最大割")
print("=" * 60)
model_square, results_square = solve_maxcut_example(
square_problem, nqubits=4, step=2, epochs=50, lr=0.01
)
# 显示最优解
best_state = max(results_square, key=results_square.get)
print(f"\n最优解: {best_state}")
print(f"出现次数: {results_square[best_state]}")
# 示例:一个简单的 4 节点图
def solve_maxcut_example(problem_dict, nqubits, step=2, epochs=50, lr=0.01):
"""
通用的 MaxCut 求解函数
参数:
problem_dict: 问题字典 {'Zi Zj': weight, ...}
nqubits: 量子比特数
step: QAOA 层数
epochs: 训练轮数
lr: 学习率
返回:
trained_model: 训练好的模型
results: 测量结果
"""
# 解析问题
pairs = []
coefs = []
for key, value in problem_dict.items():
temp = []
for item in key.split():
if item[0] == 'Z':
temp.append(int(item[1:]))
if len(temp) == 2:
pairs.append(temp)
coefs.append(value)
# 创建并训练模型
model = QAOA(nqubits, pairs, coefs, step)
trained_model, _ = trainer(model, epochs, lr)
# 测量结果
results = trained_model.measure()
return trained_model, results
# 示例:正方形图 (4个节点,4条边)
square_problem = {
'Z0 Z1': 1.0, 'Z1 Z2': 1.0,
'Z2 Z3': 1.0, 'Z3 Z0': 1.0
}
print("示例:正方形图的最大割")
print("=" * 60)
model_square, results_square = solve_maxcut_example(
square_problem, nqubits=4, step=2, epochs=50, lr=0.01
)
# 显示最优解
best_state = max(results_square, key=results_square.get)
print(f"\n最优解: {best_state}")
print(f"出现次数: {results_square[best_state]}")
5.2 与经典算法对比¶
In [ ]:
Copied!
# 经典贪心算法求解 MaxCut
def classical_maxcut_greedy(problem, nqubit):
"""
经典贪心算法求解 MaxCut
参数:
problem: 问题字典
nqubit: 节点数
返回:
best_cut: 最优分割
best_value: 最优值
"""
# 解析问题
edges = []
weights = []
for key, value in problem.items():
temp = []
for item in key.split():
if item[0] == 'Z':
temp.append(int(item[1:]))
if len(temp) == 2:
edges.append(temp)
weights.append(value)
best_cut = '0' * nqubit
best_value = 0
# 贪心算法:随机初始化,然后局部优化
for _ in range(10):
cut = np.random.randint(0, 2, nqubit)
improved = True
while improved:
improved = False
for i in range(nqubit):
current_value = sum(
w for (a, b), w in zip(edges, weights)
if cut[a] != cut[b]
)
cut[i] = 1 - cut[i]
new_value = sum(
w for (a, b), w in zip(edges, weights)
if cut[a] != cut[b]
)
if new_value <= current_value:
cut[i] = 1 - cut[i] # 恢复
else:
improved = True
value = sum(
w for (a, b), w in zip(edges, weights)
if cut[a] != cut[b]
)
if value > best_value:
best_value = value
best_cut = ''.join(map(str, cut))
return best_cut, best_value
# 对比 QAOA 和经典算法
print("\n算法对比")
print("=" * 60)
# 使用之前的问题
classical_cut, classical_value = classical_maxcut_greedy(problem, nqubit)
print(f"经典贪心算法:")
print(f" 最优解: {classical_cut}")
print(f" 目标值: {classical_value:.6f}")
# QAOA 结果
qaoa_cut = max(measurement_results, key=measurement_results.get)
qaoa_value = 0
for (i, j), coef in zip(pairs, coefs):
if qaoa_cut[::-1][i] != qaoa_cut[::-1][j]:
qaoa_value += coef
print(f"\nQAOA 算法:")
print(f" 最优解: {qaoa_cut}")
print(f" 目标值: {qaoa_value:.6f}")
print(f" 概率: {measurement_results[qaoa_cut] / sum(measurement_results.values()) * 100:.2f}%")
print(f"\n性能比: {qaoa_value / classical_value:.2%}")
# 经典贪心算法求解 MaxCut
def classical_maxcut_greedy(problem, nqubit):
"""
经典贪心算法求解 MaxCut
参数:
problem: 问题字典
nqubit: 节点数
返回:
best_cut: 最优分割
best_value: 最优值
"""
# 解析问题
edges = []
weights = []
for key, value in problem.items():
temp = []
for item in key.split():
if item[0] == 'Z':
temp.append(int(item[1:]))
if len(temp) == 2:
edges.append(temp)
weights.append(value)
best_cut = '0' * nqubit
best_value = 0
# 贪心算法:随机初始化,然后局部优化
for _ in range(10):
cut = np.random.randint(0, 2, nqubit)
improved = True
while improved:
improved = False
for i in range(nqubit):
current_value = sum(
w for (a, b), w in zip(edges, weights)
if cut[a] != cut[b]
)
cut[i] = 1 - cut[i]
new_value = sum(
w for (a, b), w in zip(edges, weights)
if cut[a] != cut[b]
)
if new_value <= current_value:
cut[i] = 1 - cut[i] # 恢复
else:
improved = True
value = sum(
w for (a, b), w in zip(edges, weights)
if cut[a] != cut[b]
)
if value > best_value:
best_value = value
best_cut = ''.join(map(str, cut))
return best_cut, best_value
# 对比 QAOA 和经典算法
print("\n算法对比")
print("=" * 60)
# 使用之前的问题
classical_cut, classical_value = classical_maxcut_greedy(problem, nqubit)
print(f"经典贪心算法:")
print(f" 最优解: {classical_cut}")
print(f" 目标值: {classical_value:.6f}")
# QAOA 结果
qaoa_cut = max(measurement_results, key=measurement_results.get)
qaoa_value = 0
for (i, j), coef in zip(pairs, coefs):
if qaoa_cut[::-1][i] != qaoa_cut[::-1][j]:
qaoa_value += coef
print(f"\nQAOA 算法:")
print(f" 最优解: {qaoa_cut}")
print(f" 目标值: {qaoa_value:.6f}")
print(f" 概率: {measurement_results[qaoa_cut] / sum(measurement_results.values()) * 100:.2f}%")
print(f"\n性能比: {qaoa_value / classical_value:.2%}")
5.3 参数敏感性分析¶
In [ ]:
Copied!
# 分析不同 QAOA 层数的影响
print("\nQAOA 层数影响分析")
print("=" * 60)
steps = [1, 2, 3, 4]
results_by_steps = []
for step in steps:
print(f"\n训练 p={step} 的 QAOA 模型...")
model = QAOA(nqubit, pairs, coefs, step)
trained_model, metrics = trainer(model, epoch=50, lr=0.01)
results = trained_model.measure()
best_state = max(results, key=results.get)
best_value = -metrics['train_loss'][-1]
results_by_steps.append({
'step': step,
'best_state': best_state,
'best_value': best_value,
'probability': results[best_state] / sum(results.values())
})
print(f" 最优值: {best_value:.6f}")
print(f" 概率: {results[best_state] / sum(results.values()) * 100:.2f}%")
# 可视化
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot([r['step'] for r in results_by_steps],
[r['best_value'] for r in results_by_steps],
'o-', linewidth=2, markersize=8)
plt.xlabel('QAOA 层数 p', fontsize=12)
plt.ylabel('目标函数值', fontsize=12)
plt.title('层数 vs 性能', fontsize=13, fontweight='bold')
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
plt.plot([r['step'] for r in results_by_steps],
[r['probability'] for r in results_by_steps],
's-', linewidth=2, markersize=8, color='orange')
plt.xlabel('QAOA 层数 p', fontsize=12)
plt.ylabel('最优解概率', fontsize=12)
plt.title('层数 vs 概率', fontsize=13, fontweight='bold')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 分析不同 QAOA 层数的影响
print("\nQAOA 层数影响分析")
print("=" * 60)
steps = [1, 2, 3, 4]
results_by_steps = []
for step in steps:
print(f"\n训练 p={step} 的 QAOA 模型...")
model = QAOA(nqubit, pairs, coefs, step)
trained_model, metrics = trainer(model, epoch=50, lr=0.01)
results = trained_model.measure()
best_state = max(results, key=results.get)
best_value = -metrics['train_loss'][-1]
results_by_steps.append({
'step': step,
'best_state': best_state,
'best_value': best_value,
'probability': results[best_state] / sum(results.values())
})
print(f" 最优值: {best_value:.6f}")
print(f" 概率: {results[best_state] / sum(results.values()) * 100:.2f}%")
# 可视化
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot([r['step'] for r in results_by_steps],
[r['best_value'] for r in results_by_steps],
'o-', linewidth=2, markersize=8)
plt.xlabel('QAOA 层数 p', fontsize=12)
plt.ylabel('目标函数值', fontsize=12)
plt.title('层数 vs 性能', fontsize=13, fontweight='bold')
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
plt.plot([r['step'] for r in results_by_steps],
[r['probability'] for r in results_by_steps],
's-', linewidth=2, markersize=8, color='orange')
plt.xlabel('QAOA 层数 p', fontsize=12)
plt.ylabel('最优解概率', fontsize=12)
plt.title('层数 vs 概率', fontsize=13, fontweight='bold')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
6. 总结与展望¶
6.1 关键要点¶
QAOA 核心思想:
- 使用交替的问题哈密顿量和混合哈密顿量
- 通过变分优化找到近似最优解
实现要素:
- 参数化量子电路 (ansatz)
- 目标函数定义 (期望值计算)
- 经典优化器 (参数更新)
性能优化:
- 合理选择 QAOA 层数
- 适当的参数初始化
- 选择合适的优化器
6.2 实际应用建议¶
- 小规模问题: 使用经典模拟器验证算法
- 中等规模: 考虑使用 NISQ 设备
- 大规模: 需要错误缓解和纠错技术
6.3 未来发展方向¶
算法改进:
- 自适应 QAOA
- 多角度 QAOA
- 问题启发式 ansatz
硬件优化:
- 噪声缓解技术
- 设备特定编译
- 量子错误纠正
应用拓展:
- 机器学习优化
- 金融组合优化
- 物流调度
6.4 参考资料¶
原始论文:
- Farhi et al., "A Quantum Approximate Optimization Algorithm" (2014)
教材:
- Nielsen & Chuang, "Quantum Computation and Quantum Information"
- Marinescu & Marinescu, "Approaching Quantum Computing"
在线资源:
- Qiskit Textbook: QAOA 章节
- PennyLane Tutorials
7. 练习题¶
练习 1: 修改问题实例¶
尝试定义一个不同的最大割问题(不同的图结构),并使用 QAOA 求解。
练习 2: 实验不同层数¶
比较 p=1, 2, 3, 5 的性能差异。讨论层数增加的影响。
练习 3: 尝试不同优化器¶
使用不同的优化器(RMSprop, SGD 等)并比较收敛速度和最终结果。
练习 4: 实现图着色问题¶
修改代码以解决 3-着色问题(用 3 种颜色给图着色)。
练习 5: 分析参数景观¶
可视化目标函数关于参数 γ 和 β 的变化,了解优化难点。
祝你学习愉快!如有问题,欢迎交流讨论。