模拟大规模量子计算机随着量子处理单元(QPUs)质量的提升而变得愈发困难。在设备规模超出经典可模拟范围后,验证结果以确保我们仍然能够信任其输出变得至关重要。
同样,在为旨在辅助量子处理器运行的各种AI模型生成大规模数据集时,我们看到需要提供所有尺度和抽象级别上有用的训练数据,这些数据由GPU加速。例子包括AI量子纠错解码器、AI编译器、用于校准和控制的AI代理,以及用于生成新设备设计的模型。
cuQuantum SDK是一套高性能库和工具,可在电路和设备级别将量子计算模拟速度提升数个数量级。最新版本cuQuantum SDK v25.11引入了加速两种新工作负载的组件:Pauli传播和稳定器模拟。这两者对于模拟大规模量子计算机都至关重要。
本文深入探讨了如何开始运行Pauli传播模拟,并加速从稳定器模拟中采样,以利用GPU加速的超计算机解决这些问题。
Pauli传播是一种相对较新的方法,用于高效模拟大规模量子电路(可包含真实量子处理器的噪声模型)的可观测值。通过将状态和可观测量表达为Pauli张量积的加权和,电路模拟可以动态丢弃对所求期望值贡献不显著的项。这允许估计那些对于精确模拟来说原本难以处理的实验量值。
许多相关的量子计算应用都围绕期望值的计算展开,例如VQE和物理动力学的量子模拟。各种精确和近似的经典模拟技术能够为大型电路计算此类可观测量,尽管在不同场景下它们会变得极其昂贵。例如,矩阵乘积态(MPS)技术是一种非常流行的用于电路模拟的近似张量网络态方法,通常不适合模拟编码二维或三维物理系统动力学的大型电路。Pauli传播是近似电路模拟工具箱中一个互补且有用的补充,适用于纯电路和含噪电路。除了被证明对于模拟近Clifford和/或高噪声电路是高效的之外,Pauli传播在模拟某些量子自旋系统演化的Trotter化电路时也显示出令人印象深刻的性能。这包括一些“效用电路”,其命名参考了其在涉及127量子位设备的某中心效用实验中的使用。确定哪些电路可以用Pauli传播高效模拟是一个持续的研究工作,其重要性不亚于该方法本身算法细节的改进。
cuQuantum 25.11通过发布这个新的cuQuantum库,提供了在NVIDIA GPU上加速Pauli传播或衍生方法的原语,使开发人员和研究人员能够推进经典电路模拟的前沿。核心功能将在以下部分描述。
初始化操作所需的库句柄和工作空间描述符:
import cupy as cp
from cuquantum.bindings import cupauliprop
from cuquantum import cudaDataType
# 创建库句柄和工作空间描述符
handle = cupauliprop.create()
workspace = cupauliprop.create_workspace_descriptor(handle)
# 为工作空间分配GPU内存
ws_size = 1024 * 1024 * 64 # 示例:64 MiB
d_ws = cp.cuda.alloc(ws_size)
cupauliprop.workspace_set_memory(
handle, workspace, cupauliprop.Memspace.DEVICE,
cupauliprop.WorkspaceKind.WORKSPACE_SCRATCH, d_ws.ptr, ws_size)要开始模拟,需要为Pauli展开式(表示为一组无符号整数及其系数的Pauli算符乘积之和)分配设备内存,并用一个可观测量(例如Z_62)初始化输入展开式。
# 辅助函数:将Pauli字符串编码为打包整数(每个量子位2位:X和Z掩码)
def encode_pauli(num_qubits, paulis, qubits):
num_ints = cupauliprop.get_num_packed_integers(num_qubits)
# 打包整数格式: [X_ints..., Z_ints...]
packed = np.zeros(num_ints * 2, dtype=np.uint64)
x_mask, z_mask = packed[:num_ints], packed[num_ints:]
for p, q in zip(paulis, qubits):
idx, bit = divmod(q, 64)
if p in (cupauliprop.PauliKind.PAULI_X, cupauliprop.PauliKind.PAULI_Y):
x_mask[idx] |= (1 << bit)
if p in (cupauliprop.PauliKind.PAULI_Z, cupauliprop.PauliKind.PAULI_Y):
z_mask[idx] |= (1 << bit)
return packed
# 1. 分配设备缓冲区
# 定义容量(最大Pauli字符串数)并分配缓冲区
max_terms = 10000
num_packed_ints = cupauliprop.get_num_packed_integers(num_qubits)
d_pauli = cp.zeros((max_terms, 2 * num_packed_ints), dtype=cp.uint64, order="C")
d_coef = cp.zeros(max_terms, dtype=cp.float64, order="C")
# 2. 填充初始可观测量 (Z_62)
encoded_pauli = encode_pauli(num_qubits, [cupauliprop.PauliKind.PAULI_Z], [62])
# 分配第一项
d_pauli[0] = cp.array(encoded_pauli)
d_coef[0] = 1.0
# 3. 创建Pauli展开式
# 输入展开式:用可观测量预填充
expansion_in = cupauliprop.create_pauli_expansion(
handle, num_qubits,
d_pauli.data.ptr, d_pauli.nbytes,
d_coef.data.ptr, d_coef.nbytes,
cudaDataType.CUDA_R_64F,
1, 1, 1 # num_terms=1, is_sorted=True, is_unique=True)
# 输出展开式:初始为空 (num_terms=0),需要自己的缓冲区
d_pauli_out = cp.zeros_like(d_pauli)
d_coef_out = cp.zeros_like(d_coef)
expansion_out = cupauliprop.create_pauli_expansion(
handle, num_qubits,
d_pauli_out.data.ptr, d_pauli_out.nbytes,
d_coef_out.data.ptr, d_coef_out.nbytes,
cudaDataType.CUDA_R_64F,
0, 0, 0)定义量子门或算符,例如Pauli旋转门。
# 在量子位0上创建Z旋转门
paulis = [cupauliprop.PauliKind.PAULI_Z]
qubits = [0]
gate = cupauliprop.create_pauli_rotation_gate_operator(
handle, theta, 1, qubits, paulis)将算符(门或噪声通道)应用于展开式,演化系统。请注意,大多数应用在所谓的海森堡绘景中工作,这意味着电路中的门以相反顺序应用于可观测量。这还需要在应用算符时将伴随参数传递为True。
# 获取输入展开式中当前项的视图
num_terms = cupauliprop.pauli_expansion_get_num_terms(handle, expansion_out)
view = cupauliprop.pauli_expansion_get_contiguous_range(
handle, expansion_in, 0, num_terms)
# 应用门:in_expansion -> gate -> out_expansion
cupauliprop.pauli_expansion_view_compute_operator_application(
handle, view, expansion_out, gate,
True, # adjoint?
False, False, # make_sorted?, keep_duplicates?
0, None, # 截断策略(可选)
workspace)计算期望值(与零状态 |0> 的迹)。
import numpy as np
result = np.zeros(1, dtype=np.float64)
# 计算迹
cupauliprop.pauli_expansion_view_compute_trace_with_zero_state(
handle, view, result.ctypes.data, workspace)结合这些方法表明,与基于CPU的代码相比,NVIDIA DGX B200 GPU提供了显著的加速。对于小的系数截断,相对于最新双路数据中心CPU上的单线程Qiskit PauliProp,观察到了多个数量级的加速。
图1. cuQuantum GPU模拟:对于127量子位某中心效用电路的π/4旋转,在NVIDIA DGX B200上与Intel Xeon Platinum 8570 CPU上的Qiskit PauliProp相比,一系列截断方案下实现了多个数量级的加速。
稳定器模拟源于Gottesman-Knill定理,该定理指出Clifford群(量子位Pauli群的正规化子)内的门可以在多项式时间内被经典高效模拟。这个Clifford群由CNOT、Hadamard和Phase门(S)组成。因此,稳定器模拟对于大规模量子纠错码的资源估计和测试至关重要。
构建稳定器模拟器有几种不同的方法,从表格模拟器到框架模拟器。cuStabilizer目前致力于提高框架模拟器中采样率的吞吐量。
框架模拟仅关注量子噪声对量子态的影响。由于量子设备不完美,可以通过在其中插入随机“噪声”门来模拟电路执行中的缺陷。如果无噪声结果已知,获取噪声结果只需要跟踪差异,或者噪声门如何改变电路输出。
事实证明,与完整电路模拟相比,这种效果更容易计算。噪声门可能插入的组合数量随着电路规模增长非常快,这意味着为了可靠地建模纠错算法,需要大量的“射击”次数。
对于有兴趣开发量子纠错码、测试新解码器或为AI解码器生成数据的用户来说,框架模拟是理想的选择。提供了API来改进采样并加速NVIDIA GPU上的任何框架模拟。cuQuantum SDK cuStabilizer库暴露了C API和Python API。虽然C API将提供更好的性能,但Python API最适合入门,因为它更灵活并为用户处理内存分配。
cuStabilizer涉及模拟的两个主要类:Circuit和FrameSimulator。电路可以接受一个包含电路指令的字符串,类似于CPU模拟器Stim中使用的格式。要创建FrameSimulator,需要指定有关电路的信息,以分配足够的资源。
import cuquantum.stabilizer as cust
# 电路信息
num_qubits = 5
num_shots = 10_000
num_measurements = 2
# 在GPU上创建电路
circ = cust.Circuit("""
H 0 1
X_ERROR(0.1) 1 2
DEPOLARIZE2(0.5) 2 3
CX 0 1 2 3
M 0 3
""")
sim = cust.FrameSimulator(
num_qubits,
num_shots,
num_measurements)
sim.apply(circ)只要模拟器有足够的可用量子位,就可以在不同的电路之间重用模拟器。以下代码将把电路应用于第一个电路circ修改后的状态。
circ2 = cust.Circuit("""
Z_ERROR(0.01) 1 4
""")
sim.apply(circ2)模拟器的状态由三个位表组成:
x_bitsz_bitsmeasurement_bits前两个表存储Pauli框架(类似于cuPauliProp的Pauli展开式,但布局不同且没有权重)。第三个表存储每个“射击”中无噪声测量与噪声测量之间的差异。
存储比特的最有效方式是将它们编码为整数值。这被称为“位打包”格式,其中内存中的每个字节存储八个有效位。虽然这种格式最有效,但操作单个比特需要在程序中额外的步骤。位打包格式不容易与常见的“数组”概念集成,因为数组通常被认为包含多个字节的值,例如int32。
为了在numpy中提供易于理解的表示形式,cuStabilizer支持bit_packed参数,可以在不同格式之间切换。如果bit_packed=False,每个比特被编码在一个uint8值中,因此使用8倍的内存。在指定输入位表时,格式对性能也很重要,如cuQuantum文档所述。
# 获取测量翻转
m_table = sim.get_measurement_bits(bit_packed=False)
print(m_table.dtype)
# uint8
print(m_table.shape)
# (2, 10000)
print(m_table)
# [[0 0 0 ... 0 0 0]
# [1 0 0 ... 0 1 1]]
x_table, z_table = sim.get_pauli_xz_bits(bit_packed=True)
print(x_table.dtype)
# uint8
print(x_table.shape)
# (5, 1252)为了便于访问底层的Pauli框架,cuStabilizer提供了一个PauliTable类,可以通过“射击”索引进行访问:
# 获取pauli表
pauli_table = sim.get_pauli_table()
num_frames_print = 5
for i in range(num_frames_print):
print(pauli_table[i])
# ...XZ
# ZXX..
# ...Z.
# .....
# ...Z.当利用采样API时,我们看到与最新数据中心CPU上的最新技术代码Google Stim相比,吞吐量可以显著提高。
cuStabilizer可以接受Stim电路作为输入,可以用它来模拟表面码电路:
import stim
p = 0.001
circ_stim = stim.Circuit.generated(
"surface_code:rotated_memory_z",
distance=5,
rounds=5,
after_clifford_depolarization=p,
after_reset_flip_probability=p,
before_measure_flip_probability=p,
before_round_data_depolarization=p,)
circ = cust.Circuit(circ_stim)
sim = cust.FrameSimulator(
circ_stim.num_qubits,
num_shots,
circ_stim.num_measurements,
num_detectors=circ_stim.num_detectors,)
sim.apply(circ)
pauli_table = sim.get_pauli_table()
for i in range(num_frames_print):
print(pauli_table[i])请注意,对于大量样本和大量量子位数,模拟效率最高。此外,当使用cupy包并将结果位表保留在GPU上时,可以获得最佳性能。
图2展示了cuStabilizer的最佳使用方式以及在NVIDIA B200 GPU和Intel Xeon Platinum 8570 CPU上的预期性能。它表明,对于代码距离31,最佳性能在大约一百万次“射击”时实现。对于大代码距离,用户可以获得1060倍的加速。
图2. 不同距离表面码且“射击”次数为100万时的运行时性能,对比了在NVIDIA DGX B200 GPU上的stim加cuStabilizer与在Intel Xeon Platinum 8570 CPU上的stim。
cuQuantum中的最新功能继续推动基于GPU的量子计算机仿真在可能实现方面的边界,实现了两种新的主要工作负载类别。这些工作负载对于中等至大规模量子设备的量子纠错、验证和确认以及算法工程至关重要。
使用 pip install cupauliprop-cu13 开始使用cuQuantum cuPauliProp。要了解更多信息,请查看cuPauliProp文档。
使用 pip install custabilizer-cu13 开始使用cuQuantum cuStabilizer。要了解更多信息,请查看cuStabilizer文档。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。