基于码容量噪声建模的量子纠错量子纠错QEC描述了一套用于检测和纠正量子计算机上量子比特发生错误的工具。本示例将介绍 CUDA-Q QEC 库如何处理量子纠错中最常见的两种对象稳定子码和解码器。稳定子码是经典纠错中线性码的量子推广后者使用奇偶校验来检测噪声比特上的错误。在量子纠错中我们将在辅助量子比特上执行稳定子测量以检查数据量子比特的奇偶性。这些稳定子测量是非破坏性的因此允许我们在不破坏量子信息的前提下检查量子比特的相对奇偶性。例如如果我们将两个量子比特制备在态 Ψa∣00⟩b∣11⟩ 我们可能想要检查是否发生了比特翻转错误。我们可以测量稳定子 ZZ 如果没有错误或偶数个错误它将返回 0但如果任一量子比特发生了翻转它将返回 1。这就是我们如何在量子计算中执行奇偶校验的方法而无需执行会破坏叠加态的破坏性测量。这些测量在物理上如何执行可以在电路级噪声量子纠错示例中看到。我们可以通过稳定子算符列表如上文的 ZZ 或等价的奇偶校验矩阵来指定一个稳定子码。我们可以将奇偶校验矩阵的列视为可能发生的错误类型。在这种情况下每个量子比特可能经历比特翻转 X 错误或相位翻转 Z 错误因此奇偶校验矩阵将有 2N 列其中 N 是数据量子比特的数量。每一行代表一个稳定子或一个奇偶校验。取值为 0 或 1其中 1 表示对应列参与该奇偶校验0 表示不参与。因此如果单个 X/Z 错误发生在某个量子比特上奇偶校验矩阵中受支持的行将被触发。这被称为征状syndrome是一串 0 和 1对应哪些奇偶校验被违反。稳定子码的一个特殊类别称为CSSCalderbank-Shor-Steane码这意味着它们的奇偶校验矩阵的 X 分量和 Z 分量可以分离。这就引出了解码。解码是求解以下问题的过程给定一个征状最可能的底层错误是什么存在许多解码算法但本示例将使用简单的单错误查找表。这意味着解码器将枚举每个单错误比特串对应的征状。然后给定一个征状它将查找对应的错误串并返回结果。最后我们需要一种生成错误的方法。本示例将介绍码容量噪声模型其中每个量子比特以某个概率 p 独立且同分布地发生 X 或 Z 错误。CUDA-Q QEC 实现以下是如何使用 CUDA-Q QEC 在 Python 和 C 中执行码容量噪声模型实验Pythonimport numpy as np import cudaq_qec as qec # 获取一个量子纠错码 steane qec.get_code(steane) # 获取码的奇偶校验矩阵 # 可以获取完整的码或者对于 CSS 码 # 仅获取 X 或 Z 分量 Hz steane.get_parity_z() print(fHz:\n{Hz}) observable steane.get_observables_z() print(fobservable:\n{observable}) # 错误概率 p 0.1 # 获取解码器 decoder qec.get_decoder(single_error_lut, Hz) # 执行码容量噪声模型数值实验 nShots 10 nLogicalErrors 0 for i in range(nShots): print(fshot: {i}) # 生成噪声数据 data qec.generate_random_bit_flips(Hz.shape[1], p) print(fdata: {data}) # 计算哪些征状被触发 syndrome Hz data % 2 print(fsyndrome: {syndrome}) # 解码征状以预测数据发生了什么 results decoder.decode(syndrome) convergence results.converged result results.result data_prediction np.array(result, dtypenp.uint8) print(fdata_prediction: {data_prediction}) # 检查此预测是否翻转了可观测量 predicted_observable observable data_prediction % 2 print(fpredicted_observable: {predicted_observable}) # 检查可观测量是否实际被翻转 actual_observable observable data % 2 print(factual_observable: {actual_observable}) if (predicted_observable ! actual_observable): nLogicalErrors 1 # 统计解码器未能纠正错误的次数 print(f{nLogicalErrors} logical errors in {nShots} shots\n) # 也可以通过单行代码生成征状和数据 syndromes, data qec.sample_code_capacity(Hz, nShots, p) print(From sample function:) print(syndromes:\n, syndromes) print(data:\n, data)C// This example shows the primary cudaq::qec types: // decoder, code // // Compile and run with // nvq --enable-mlir --targetstim -lcudaq-qec code_capacity_noise.cpp // ./a.out #include algorithm #include cmath #include random #include cudaq.h #include cudaq/qec/decoder.h #include cudaq/qec/experiments.h int main() { auto steane cudaq::qec::get_code(steane); auto Hz steane-get_parity_z(); std::vectorsize_t t_shape Hz.shape(); std::cout Hz.shape():\n; for (size_t elem : t_shape) std::cout elem ; std::cout \n; std::cout Hz:\n; Hz.dump(); auto Lz steane-get_observables_x(); std::cout Lz:\n; Lz.dump(); double p 0.2; size_t nShots 5; auto lut_decoder cudaq::qec::get_decoder(single_error_lut, Hz); std::cout nShots: nShots \n; // May want a order-2 tensor of syndromes // access tensor by stride to write in an entire syndrome cudaqx::tensoruint8_t syndrome({Hz.shape()[0]}); int nErrors 0; for (size_t shot 0; shot nShots; shot) { std::cout shot: shot \n; auto shot_data cudaq::qec::generate_random_bit_flips(Hz.shape()[1], p); std::cout shot data\n; shot_data.dump(); auto observable_z_data Lz.dot(shot_data); observable_z_data observable_z_data % 2; std::cout Data Lz state:\n; observable_z_data.dump(); auto syndrome Hz.dot(shot_data); syndrome syndrome % 2; std::cout syndrome:\n; syndrome.dump(); auto result lut_decoder-decode(syndrome); cudaqx::tensoruint8_t result_tensor; // result.result is a std::vectorfloat_t, of soft information. Well // convert this to hard information and store as a tensoruint8_t. cudaq::qec::convert_vec_soft_to_tensor_hard(result.result, result_tensor); std::cout decode result:\n; result_tensor.dump(); // check observable result auto decoded_observable_z Lz.dot(result_tensor); std::cout decoded observable:\n; decoded_observable_z.dump(); // check how many observable operators were decoded correctly // observable_z_data decoded_observable_z This maps onto element wise // addition (mod 2) auto observable_flips decoded_observable_z observable_z_data; observable_flips observable_flips % 2; std::cout Logical errors:\n; observable_flips.dump(); std::cout \n; // shot counts as a observable error unless all observables are correct if (observable_flips.any()) { nErrors; } } std::cout Total logical errors: nErrors \n; // Full data gen in function call auto [syn, data] cudaq::qec::sample_code_capacity(Hz, nShots, p); std::cout Numerical experiment:\n; std::cout Data:\n; data.dump(); std::cout Syn:\n; syn.dump(); }代码解释1. 量子纠错码类型CUDA-Q QEC 的核心是qec.code类型它包含给定码的相关数据。特别地这代表一组表示单个逻辑量子比特的量子比特集合。这里我们获取最著名的量子纠错码之一——Steane 码使用qec.get_code函数。我们可以用code.get_stabilizers()函数从码中获取稳定子。在本示例中我们获取码的奇偶校验矩阵。由于 Steane 码是 CSS 码我们可以仅提取奇偶校验矩阵的 Z 分量。这里我们看到该矩阵有 3 行 7 列这意味着有 7 个数据量子比特7 种可能的单比特翻转错误和 3 个 Z -稳定子奇偶校验。注意Z 稳定子用于检测 X 型错误。最后我们获取码的逻辑 Z 可观测量。这将允许我们查看逻辑量子比特的 Z 可观测量是否发生翻转。2. 解码器类型可以通过qec.get_decoder调用获取单错误查找表LUT解码器。传入奇偶校验矩阵为解码器提供将征状与底层错误机制关联所需的信息。一旦解码器构建完成调用decoder.decode(syndrome)成员函数返回给定征状的预测错误。3. 噪声模型为了生成噪声数据我们调用qec.generate_random_bit_flips(nBits, p)它将返回一个比特数组其中每个比特以概率 p 被翻转为 1以概率 1−p 保持为 0。由于我们使用的是 Z 奇偶校验矩阵 HZ​ 我们希望模拟 7 个数据量子比特上的随机 X 错误。4. 逻辑错误一旦我们有了噪声数据我们通过将噪声数据向量与奇偶校验矩阵相乘模 2来查看产生的征状。从这个征状出发我们查看解码器预测数据发生了什么错误。要归类为逻辑错误解码器不需要精确猜测数据发生了什么而是需要判断逻辑可观测量是否发生翻转。如果解码器成功猜中这一点我们就纠正了量子错误。否则我们就产生了逻辑错误。5. 进一步自动化虽然这个工作流程适合逐步观察但qec.sample_code_capacityAPI 提供了批量生成噪声数据及其对应征状的功能。总结CUDA-Q QEC 库为数值量子纠错实验提供了一个平台。qec.code可用于分析各种量子纠错码库提供的或用户自定义的配合各种解码器库提供的或用户自定义的。CUDA-Q QEC 库还提供工具来加速生成噪声数据和征状的自动化过程。https://nvidia.github.io/cudaqx/examples_rst/qec/code_capacity_noise.html