你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。

运行和解码逻辑程序

使用 Microsoft Quantum 开发工具包(QDK)将逻辑Q#或OpenQASM程序编译为Quantum Intermediate Representation(QIR),然后使用qdk.simulation.run_qir通过qodec运行该程序。 设置噪音模型,然后选择内置 syndrome 解码器或可选的 deq 线路级解码器。

Prerequisites

在开始之前完成这些任务:

这些Q#示例使用名为C4的qodec,其中包含protocol代码的说明。 若要创建一个,请参阅 开发和验证 QEC 协议。

import qodec
from qdk import ec

c4 = qodec.Code(
    "C4",
    stabilizers=["X_0 X_1 X_2 X_3", "Z_0 Z_1 Z_2 Z_3"],
    x=["X_0 X_1", "X_0 X_2"],
    z=["Z_0 Z_2", "Z_0 Z_1"],
)

protocol = ec.build_qodec(
    c4,
    strategy="bare-css/v1",
    strict=False,
)

report = ec.audit(protocol)
if report.errors or report.warnings:
    raise ValueError(str(report))

在运行之前对qodec进行检查。 运行时信任 qodec 的检查、读数、标志和帧方程,因此无效声明可能会导致不正确的逻辑结果或解码器失败。

此工作流使用 qdk.simulation.run_qir 并配合 qodec。 该工具 qodec 使用单独的 StimPython 包 来分析 Stim 格式的小工具线路。 它不调用 qdk.stim.run。 在 LinuxArm64 或 WindowsArm64 上,从源代码安装 Stim,然后在运行具有 Stim-format 线路的 qodec 之前重启 Python 或 Jupyter 内核。

将 QIR 调用与 qodec 指令匹配

将 QIR 程序中的每个量子调用与 qodec 的顶层指令集中的一个指令匹配。 指令助记必须与 QIR 被调用方名称完全匹配。

下表显示了示例映射:

源调用 必需的 qodec 助记符
Q# X(q) 或 OpenQASMx q; __quantum__qis__x__body
Q# H(q) 或 OpenQASMh q; __quantum__qis__h__body
Q# CNOT(a, b) 或 OpenQASMcx a, b; __quantum__qis__cx__body
Q# Adjoint S(q) 或 OpenQASMsdg q; __quantum__qis__s__adj
Q# M(q) 或 OpenQASMc = measure q; __quantum__qis__m__body
Q# 固有操作 Foo Foo
OpenQASM 内部网关或命名的 deffoo foo

QIR 参数按以下顺序绑定到 qodec 指令元素:

  1. 量子位指针参数绑定到块操作数。
  2. 标量参数绑定到指令参数。
  3. 结果指针参数接收指令结果和返回的任何标志。

如果量子调用与顶部指令集中的指令不匹配,编译器将引发错误。

在 中声明逻辑指令

基于C4代码构建的qodec提供自定义指令,例如prepare_z_all、measure_z_all、syndrome和cx_all。 使用相同的名称声明 Q# 内部操作。

使用自适应目标配置文件进行初始化 Q# ,因为自定义度量操作需要它。

from qdk import TargetProfile, qsharp

qsharp.init(target_profile=TargetProfile.Adaptive_RI)

将 C4 指令声明为 Q# 操作。

qsharp.eval("""
@Measurement()
operation prepare_z_all(block : Qubit) : Result { body intrinsic; }

@Measurement()
operation prepare_x_all(block : Qubit) : Result { body intrinsic; }

@Measurement()
operation measure_z_all(block : Qubit) : (Result, Result) { body intrinsic; }

@Measurement()
operation measure_x_all(block : Qubit) : (Result, Result) { body intrinsic; }

operation cx_all(control : Qubit, target : Qubit) : Unit { body intrinsic; }
operation syndrome(block : Qubit) : Unit { body intrinsic; }
""")

在此程序中,每个 Q#Qubit 都表示一个编码块。 该 C4 块有两个逻辑量子比特,因此每个度量操作返回两个逻辑结果。 每个准备操作返回一个拒绝标志。

编译逻辑 Q# 实验

定义状态准备和测量(SPAM)实验。

qsharp.eval("""
operation TestSpamZ() : (Result[], Result[]) {
    use block = Qubit();
    let reject = prepare_z_all(block);
    let (left, right) = measure_z_all(block);
    ([reject], [left, right])
}
""")

将 TestSpamZ 操作编译为 QIR.

qir = str(qsharp.compile("TestSpamZ()"))

QIR 保留了对 prepare_z_all 和 measure_z_all 的调用。 当程序运行时,qodec 运行时将这些名称与顶层指令集匹配,并将每个逻辑调用替换为其小工具。

使用内置解码器运行

调用 run_qir 并传递 qodec。 None如果是decoder,运行时使用内置的最小权重Pauli解码器。

from qdk.simulation import run_qir

results = run_qir(
    qir,
    qodec=protocol,
    type="stabilizer",
    shots=100,
    seed=42,
)

print(results[:5])

若要运行一个 qodec,请使用以下模拟器选项:

type 值 Behavior
None 或 "stabilizer" 使用稳定子后端
"cpu" 对受支持的程序使用状态向量后端
"gpu" 不支持与 qodec 一起使用

运行时在首次使用每个程序量子位之前准备它,除非第一条指令创建其块。 准备指令具有输出块,没有输入块,以及稳定逻辑 Z 的操作。

单量子位 Pauli 解码器校正和 qodec 帧更新使用无噪声 Pauli 帧,而不是有噪声的物理门。

配置模拟器噪声

在模拟器操作上定义噪声,而不是在 qodec 指令名称上。 运行时将每个底层指令转换为一个或多个模拟器操作,因此噪声设置适用于这些操作。

该 C4 演练指南使用以下映射:

底层指令 模拟器操作 NoiseConfig 字段
R 准备或重置为 Z+ mresetz
H Hadamard h
CX Controlled X cx
M Z 基测量 mresetz

创建简单的 1% 干扰模型:

from qdk.simulation import NoiseConfig

noise = NoiseConfig()
for gate in (noise.h, noise.cx):
    gate.set_depolarizing(0.01)
noise.mresetz.x = 0.01

使用噪声运行试验:

results = run_qir(
    qir,
    qodec=protocol,
    noise=noise,
    type="stabilizer",
    shots=1000,
    seed=42,
    on_shot_failure="discard",
)

mresetz 上的噪声在测量或准备后作用于量子位。 它不会翻转模拟器已记录的测量位。 要研究记录的位翻转,请使用 qodec 读出故障模型和小工具分析。

控制拍摄失败

使用 on_shot_failure 选择当 qodec 标志、解码器或读出未产生可接受的拍摄时运行时的响应方式。

Value Behavior
"discard" 从返回的列表中省略失败的拍摄。 此值为默认值。
"raise" 在第一次拍摄失败时停止。
"retry" 重启失败的拍摄,最多 max_retries 次额外尝试。

使用 "discard" 策略,shots 参数计算尝试的拍摄次数,因此返回的列表中的项目数可能少于 shots。

仅当逻辑程序未接收到该标志时,标志才会触发拍摄策略。 如果 Q#@Measurement() 操作返回额外的 Result 作为标志,程序会接收该标志并决定是否拒绝拍摄。

在此 TestSpamZ 示例中,程序返回第一个数组中的准备标志。 因此,该 on_shot_failure="discard" 设置不会删除返回的标志为 One 的行。

选择解码器

若要选择如何解码错误,请使用 decoder 参数为每个编码层传入用于准备解码器的可调用对象。

QDK 具有以下解码器:

解码器 使用它来
默认综合征解码器 从综合征证据推断最小权重 Pauli 校正。 使用 decoder=None。
帧解码器 跟踪无噪音帧,而不会推断物理故障。 使用 prepare_frame_decoder。
deq 解码器 使用 deq 解码线路级故障模型。 使用 prepare_deq_decoder。

使用默认 deq 配置

安装 deq 和 deq-runtime,然后传入 prepare_deq_decoder。

from qdk.simulation.decoders import prepare_deq_decoder

results = run_qir(
    qir,
    qodec=protocol,
    decoder=prepare_deq_decoder,
    noise=noise,
    type="stabilizer",
    shots=1000,
    seed=42,
    on_shot_failure="discard",
)

默认情况下, deq 适配器使用中继信念传播和窗口协调器。 若要更改这些设置,请使用运行时工厂。

配置单体 deq 运行时

若要直接选择 deq 设置,请将运行时工厂传递给 prepare_deq_decoder。

from functools import partial
from deq.runtime import Runtime
from qdk.simulation import decoders


def make_runtime(seed: int) -> Runtime:
    return Runtime(
        decoder="black-box-relay-bp",
        decoder_config={"parallel": 1, "seed": seed},
        coordinator="monolithic",
        controller="jit",
    )


results = run_qir(
    qir,
    qodec=protocol,
    decoder=partial(
        decoders.prepare_deq_decoder,
        runtime_factory=make_runtime,
    ),
    noise=noise,
    type="stabilizer",
    shots=1000,
    seed=42,
    on_shot_failure="discard",
)

从每个工厂调用中返回一个新 Runtime 值。 QDK 启动并关闭每个运行时。

Note

适配器 deq 需要:

  • 一个编码层
  • 与度量无关 Clifford 的执行跟踪
  • 稳定子后端
  • 无量子比特丢失的干扰模型

适配器 deq 不支持重试策略。 不支持的程序会引发错误,而不是回退到另一个解码器。

拒绝低置信度 deq 结果

适配器 deq 为每个逻辑读数返回一个分数。 较低的分数表示对所选更正的偏好更强。 这些分数并不是经校准的失效概率。

若要拒绝低置信度结果,在准备解码器时传递 max_readout_score ,如以下示例所示:

from functools import partial
from qdk.simulation import decoders

scored_decoder = partial(
    decoders.prepare_deq_decoder,
    max_readout_score=0.2,
)

results = run_qir(
    qir,
    qodec=protocol,
    decoder=scored_decoder,
    noise=noise,
    type="stabilizer",
    shots=1000,
    on_shot_failure="discard",
)

仅当每个返回的逻辑读出的分数在阈值或低于阈值时,运行时才接受拍摄。 阈值不适用于原始标志或未使用的中间读取。

基于分数的拒绝可以降低可接受拍摄的逻辑错误率。 但它也会减少可接受的镜头数量,并增加解码器的工作量。

编译 OpenQASM 程序

标准OpenQASM操作使用与标准Q#操作相同的QIR助记规则。

from qdk import TargetProfile, openqasm

source = """
OPENQASM 3.0;
include "stdgates.inc";
qubit q;
bit c;
x q;
c = measure q;
"""

qir = openqasm.compile(
    source,
    target_profile=TargetProfile.Adaptive_RI,
)

print(qir)

要通过 qodec 运行此程序,请确保 qodec 的顶层指令集具有 __quantum__qis__x__body 和 __quantum__qis__m__body,并具有兼容的块操作数和一个测量结果。

OpenQASM 名为foo的内在门或子例程使用助记符foo。 量子位参数绑定到块操作数。 float、int 和 bool 参数按声明顺序绑定到指令参数。

OpenQASM 对 qodec 调用有以下限制:

  • 自定义内部函数不能返回 QIRResult。 要获得一个测量结果,请使用标准 measure 语句。
  • OpenQASM 程序无法运行自定义度量、从一次调用接收多个结果或接收返回的标志。 在这些情况下,请使用 Q#@Measurement() 操作。
  • 运行时控制 OpenQASM 程序无法接收的任何标志,并且这些标志可以拒绝拍摄。

有关详细映射示例,请参阅以下 QDK 笔记本:

查看要求和限制

在运行大型试验之前,请查看这些要求、限制和结果差异。

要求:

  • 每个 QIR 量子调用都必须解析为顶层 qodec 指令。
  • 在运行流程之前对其进行审核。 运行时 qodec 信任你编写的公式,并且不检查它们。

Limitations:

  • GPU 模拟不支持 qodec 执行。
  • 默认构件故障集可以呈指数级增长,因此确切的距离搜索可能需要很长时间。
  • deq 和 Stim 包具有平台限制。 有关详细信息,请参阅安装 QDK 以进行错误更正。

结果差异:

  • 确切的输出计数可能因解码器而异。
  • 放弃和重试策略可以更改返回的结果分布。

有关噪音扫描、deq解码和基于置信度拒绝的完整Q#试验,请参阅 QEC 协议演练。