# 云执行:把线路交给量子平台 本地模拟回答"线路对不对",云执行回答"线路在真实平台上跑出来是什么样"。本章走通 UnifiedQuantum 的远端任务主链路:`submit_task` 提交 → `wait_for_result` 取结果 → `query_task` 查状态,讲清后端命名规则、异步轮询、统一结果的后处理、批量提交、凭据配置,最后用 `uniqc` 命令行把整套流程串起来。 本页示例基于 `unified-quantum` **0.1.0**,所有输出均为实际运行结果。**本章约定**:可运行的示例全部跑在 dummy 后端家族上(不需要任何 token、不联网);涉及真实云平台的代码以"模板"标注呈现,需要你先配置平台凭据(见[第 6 节](#uqt-cloud-config))才能运行。 测量统计与期望值的理论背景见[量子计算算法教程·量子计算基础](https://chenzhaoyun.com/quantum-tutorial/ch01-basics/quantum-computing-basics.html)。如果你还没读过[快速开始](../ch01-getting-started/quickstart.md)第 5 节的统一执行入口,建议先补上。 :::{admonition} 本课知识点 :class: tip 1. **[云执行工作流](#uqt-cloud-workflow)**——能写出 `submit_task → wait_for_result → query_task` 的完整调用链,并解释每一步的返回值。 2. **[后端命名与平台选择](#uqt-cloud-backends)**——能解释 `provider:chip-name` 规范格式与 dummy 家族各成员的语义,为给定线路选择合适的后端。 3. **[异步轮询与任务状态](#uqt-cloud-polling)**——能用 `poll_result` 与 `TaskStatus` 构造非阻塞轮询循环,并正确处理失败任务。 4. **[统一结果与期望值后处理](#uqt-cloud-result)**——能从 `UnifiedResult` 读取计数并计算 Pauli 期望值,解释 bitstring 的 LSB 约定。 5. **[批量提交](#uqt-cloud-batch)**——能用 `submit_batch` 一次提交整组线路,并按提交顺序收集各线路的结果。 6. **[凭据与配置管理](#uqt-cloud-config)**——能写出配置各平台 token 的命令,描述 `~/.uniqc/config.yaml` 的 profile 结构。 7. **[命令行工作流](#uqt-cloud-cli)**——能用 `uniqc submit/result/task` 完成"提交—取结果—管理"的命令行闭环。 ::: (uqt-cloud-workflow)= ## 1. 云执行工作流:submit → wait → query 三个函数构成主链路:`submit_task` 把线路交给后端并立即返回任务号(`uqt_` 开头);`wait_for_result` 阻塞等待任务完成并返回统一结果;`query_task` 查询任务的元信息。先用最熟悉的 Bell 线路在 dummy 后端上走一遍: ```python from uniqc import Circuit, submit_task, wait_for_result, query_task circuit = Circuit() circuit.h(0) circuit.cnot(0, 1) circuit.measure(0, 1) task_id = submit_task(circuit, backend="dummy:local:simulator", shots=1024) print("task_id:", task_id) result = wait_for_result(task_id, timeout=60) print("counts:", dict(result.counts)) print("probabilities:", {k: round(v, 4) for k, v in result.probabilities.items()}) print("platform:", result.platform, "| backend:", result.backend_name) info = query_task(task_id) print("status:", info.status) ``` ```text task_id: uqt_d05cefcee20a442dad63f0eb4f40408e counts: {'00': 512, '11': 512} probabilities: {'00': 0.5, '11': 0.5} platform: dummy | backend: dummy:local:simulator status: success ``` (`task_id` 每次运行都不同;dummy 后端按态矢量的精确概率确定性生成计数,所以同一线路的计数可复现。) 调用链的设计是**异步**的:`submit_task` 只负责登记任务,不等结果。dummy 后端在本地同步执行、瞬间完成,所以上面的 `wait_for_result` 立刻返回;真实云平台则要排队,可能等几秒到几小时。 `circuit` 参数除了 `Circuit` 对象,也接受 OriginIR 字符串(`QINIT` 开头)、OpenQASM 2.0 字符串(`OPENQASM` 开头)或 `qiskit.QuantumCircuit`,提交时自动检测转换——任意工具产出的线路文本都能走这个入口。 提交到真实云平台,调用链完全相同,只换 `backend`(以下为**模板,本页未运行**,需要真实 token): ```python # 模板:提交到 OriginQ 真机(需要先配置 token,见第 6 节) task_id = submit_task(circuit, backend="originq:WK_C180", shots=1024) result = wait_for_result(task_id, timeout=600, poll_interval=5) print(result.counts) ``` (uqt-cloud-backends)= ## 2. 后端命名与平台选择 `backend` 参数是字符串,两种形态: - **云平台后端**:`provider:chip-name` 规范格式,如 `originq:WK_C180`、`ibm:ibm_brisbane`。仅传裸平台名(`'originq'`)会被拒绝,错误信息会列出本地缓存里可用的 chip。 - **本地 dummy 后端**:以 `dummy` 开头,不联网、不需要凭据,是本章的主角。 | 后端 ID | 含义 | |---------|------| | `dummy` 或 `dummy:local:simulator` | 无约束、无噪声的本地虚拟机 | | `dummy:local:virtual-line-N` | N 比特线性拓扑(链),无噪声 | | `dummy:local:virtual-grid-RxC` | R×C 网格拓扑,无噪声 | | `dummy:local:mps-linear-N` | N 比特 MPS 模拟器(线性链) | | `dummy::` | 复用真实芯片的拓扑与标定数据,本地含噪模拟(规则型写法,不在后端列表中) | | `originq:WK_C180` | OriginQ 180 比特真机(需 token) | | `originq:full_amplitude` | OriginQ 全振幅云模拟器(需 token) | | `ibm:ibm_brisbane` | IBM 真机(需 token) | 同一份代码换后端即可迁移。dummy 家族内部对比: ```python from uniqc import Circuit, submit_task, wait_for_result from uniqc import list_backends print("registered backends:", list_backends()) bell = Circuit() bell.h(0) bell.cnot(0, 1) bell.measure(0, 1) for backend_id in ["dummy:local:simulator", "dummy:local:virtual-line-3"]: task_id = submit_task(bell, backend=backend_id, shots=1024) result = wait_for_result(task_id) print(f"{backend_id:32s} -> {dict(result.counts)}") ``` ```text registered backends: ['dummy', 'ibm', 'logicalqubit', 'originq', 'quark', 'tianyan'] dummy:local:simulator -> {'00': 512, '11': 512} dummy:local:virtual-line-3 -> {'00': 512, '11': 512} ``` 两个后端计数相同,因为 `CNOT q[0], q[1]` 恰好落在链 0–1–2 的相邻边上;`virtual-line-3` 与 `simulator` 的区别在于它**强制执行拓扑约束**——双比特门只能落在相邻比特对上,第 3 节会看到违反约束的下场。 各平台定位速览: | 平台 | 定位 | 额外依赖 | |------|------|---------| | OriginQ Cloud | 主生产路径 | 无 | | IBM Quantum | 国际平台 | 无(qiskit 是核心依赖) | | Tianyan(天衍) | 国内平台(QCIS 格式) | 无 | | LogicalQubit | 逻辑比特平台 | 无 | | QuarkStudio / Quark | 第三方云平台 | `pip install unified-quantum[quark]`(Python ≥ 3.12) | | Dummy | 本地模拟 | 无 | `dummy::`(如 `dummy:originq:WK_C180`)是一类特别的成员:先用真实芯片的拓扑与标定数据做编译,再在本地注入该芯片的噪声执行,让你在花真机额度之前预估线路在该芯片上的表现。它需要本地已有对应平台的芯片缓存(配置 token 后运行 `uniqc backend list -p originq` 会自动拉取),含噪模拟的细节见[第 3 章](../ch03-simulation/index.md)。 (uqt-cloud-polling)= ## 3. 异步轮询与任务状态 任务有五种状态: | `TaskStatus` | 含义 | |--------------|------| | `PENDING` | 已登记、尚未开始 | | `RUNNING` | 执行中 | | `SUCCESS` | 成功完成 | | `FAILED` | 执行失败 | | `CANCELLED` | 已取消 | `poll_result` 是**非阻塞**查询:立即返回当前状态,适合放进自己的循环里;`wait_for_result` 与 `get_result` 是阻塞接口,内部替你轮询到终态: ```python import time from uniqc import Circuit, submit_task, poll_result, wait_for_result, TaskStatus circuit = Circuit() circuit.h(0) circuit.cnot(0, 1) circuit.measure(0, 1) task_id = submit_task(circuit, backend="dummy:local:simulator", shots=1024) info = poll_result(task_id) # 非阻塞:立即返回当前状态 print("poll:", info.status) while info.status in (TaskStatus.PENDING, TaskStatus.RUNNING): time.sleep(0.5) info = poll_result(task_id) print("final:", info.status) result = wait_for_result(task_id) # 已完成的任务立刻返回结果 print(dict(result.counts)) ``` ```text poll: success final: success {'00': 512, '11': 512} ``` 注意第一次 `poll_result` 就返回 `success`——dummy 在提交时就同步算完了。真实云平台上这段循环会先看到 `running`、转若干圈后到 `success`,这正是轮询模式存在的意义:你可以在等待间隙做别的事(提交下一批线路、写文件)。 再看失败路径。把一个双比特门放到 `virtual-line-3` 的不相邻比特对上: ```python from uniqc import Circuit, submit_task, poll_result, wait_for_result, TaskFailedError bad = Circuit() bad.h(0) bad.cnot(0, 2) # q[0] 与 q[2] 在 line-3 拓扑上不相邻 bad.measure(0, 1, 2) task_id = submit_task(bad, backend="dummy:local:virtual-line-3", shots=100) print("poll:", poll_result(task_id).status) try: wait_for_result(task_id) except TaskFailedError as e: print("TaskFailedError:", str(e)[:110], "…") ``` ```text poll: failed TaskFailedError: Task 'uqt_3e43e1439c4d4c85b232946256924267' failed on backend 'dummy:local:virtual-line-3': shard 0: Error in … ``` 拓扑违规不会在提交时被拦下,而是在执行时失败:`poll_result` 给出 `failed`,`wait_for_result` 默认抛 `TaskFailedError`(传 `raise_on_failure=False` 则返回 `None`,适合批量任务"先收尸、后分析");等超时则抛 `TaskTimeoutError`。错误信息里带着后端名、分片号与出错的门,足以定位问题。 所有任务(含结果与元数据)都落在本地缓存 `~/.uniqc/cache/tasks.sqlite` 里,进程退出、机器重启都不丢: ```python from uniqc import list_tasks for t in list_tasks(limit=3): print(t.task_id[:16] + "…", t.status, t.backend) ``` ```text uqt_4993d4da58b4… failed dummy:local:virtual-line-3 uqt_e1b1a204e677… failed dummy:local:virtual-line-3 uqt_8af4519d11a9… success dummy:local:virtual-grid-2x2 ``` (列表内容取决于本机任务历史,每次运行略有不同。)配套的清理函数是 `clear_completed_tasks()` 与 `clear_cache()`。 (uqt-cloud-result)= ## 4. 统一结果与期望值后处理 各平台原生结果格式五花八门(IBM 的小端 BitArray、天衍的测量比特标签序……),`UnifiedResult` 把它们全部归一:`counts` 是 `{bitstring: 计数}`,`probabilities` 是归一化概率,另有 `shots`、`platform`、`backend_name`、`task_id`、`execution_time`、`error_message` 等元信息字段。 先跑一个三比特线路,再做两类后处理: ```python import math from uniqc import ( Circuit, calculate_expectation, shots2prob, submit_task, wait_for_result, ) circuit = Circuit() circuit.x(0) # q[0] -> |1> circuit.rx(1, math.pi) # q[1] -> |1>(相位无关) circuit.ry(2, math.pi / 2) circuit.cz(1, 2) # q[1]=|1> 时对 q[2] 加 Z circuit.measure(0, 1, 2) result = wait_for_result(submit_task(circuit, backend="dummy:local:simulator", shots=1000)) print("counts:", dict(result.counts)) print("probabilities:", {k: round(v, 4) for k, v in result.probabilities.items()}) print(f" = {calculate_expectation(result.probabilities, 'ZII'):+.4f}") print(f" = {calculate_expectation(result.probabilities, 'IIZ'):+.4f}") print("shots2prob:", {k: round(float(v), 4) for k, v in shots2prob(result.counts).items()}) ``` ```text counts: {'001': 0, '011': 500, '101': 0, '111': 500} probabilities: {'001': 0.0, '011': 0.5, '101': 0.0, '111': 0.5} = +0.0000 = -1.0000 shots2prob: {'001': 0.0, '011': 0.5, '101': 0.0, '111': 0.5} ``` 三个值得停下来说的细节: **bitstring 的 LSB 约定。** `c[0]`(第一次 `measure` 的结果)永远是 bitstring **最右侧**字符,`c[N-1]` 在最左侧。本例 `measure(0, 1, 2)` 依次把 q[0]、q[1]、q[2] 写入 c[0]、c[1]、c[2],所以 `'011'` 读作 c[2]=0、c[1]=1、c[0]=1。这条约定对所有平台生效(由发布测试强制保护),下游代码不需要做 `[::-1]` 之类的手工翻转。自检探针:`x(0)` 后先测 q[0] 再测 q[1],任何后端的支配性结果都必须是 `'01'`—— ```python probe = Circuit() probe.x(0) probe.measure(0) # -> c[0],bitstring 最右字符 probe.measure(1) # -> c[1] probe_result = wait_for_result(submit_task(probe, backend="dummy:local:simulator", shots=1024)) print("endianness probe:", dict(probe_result.counts)) ``` ```text endianness probe: {'01': 1024} ``` **期望值。** `calculate_expectation(probabilities, 'IIZ')` 按 Pauli 串计算 ⟨Z⟩ 型期望值,Pauli 串与 bitstring 同序(最左字符对应最高位)。本例 q[0] 恒为 |1⟩,故 ⟨IIZ⟩ = −1;q[2] 处在 (|0⟩−|1⟩)/√2 上完全随机,故 ⟨ZII⟩ = 0。期望值是变分算法的燃料——为什么 VQE 需要反复测 ⟨H⟩,见[量子计算算法教程·VQE](https://chenzhaoyun.com/quantum-tutorial/ch08-qml/vqe-tutorial.html)与本站[第 7 章实操](../ch07-labs/index.md)。 **0 计数的键。** `counts` 里出现了 `'001'`、`'101'` 两个 0 计数键:`RX(math.pi)` 中浮点数 π 让 cos(π/2) ≈ 6×10⁻¹⁷ ≠ 0,q[1]=0 的分支留下了微小振幅,态矢量模拟器便把它计入支集。真实硬件只返回实际测到的结果,通常不会出现 0 计数键;后处理代码不应假设键集合等于支集。 (uqt-cloud-batch)= ## 5. 批量提交:submit_batch 参数扫描、变分算法的每个迭代步都要提交一组线路。`submit_batch` 接受线路列表,返回**一个** `uqt_*` 批任务号;`wait_for_result` 对批任务返回 `list[UnifiedResult]`,顺序与提交顺序一致: ```python import math from uniqc import Circuit, submit_batch, wait_for_result circuits = [] for i in range(3): c = Circuit() c.h(0) c.rx(1, i * math.pi / 4) c.cnot(0, 1) c.measure(0, 1) circuits.append(c) batch_id = submit_batch(circuits, backend="dummy:local:simulator", shots=1000) print("batch_id:", batch_id) results = wait_for_result(batch_id) # 批量任务返回 list[UnifiedResult] print("results:", len(results)) for i, r in enumerate(results): print(i, dict(r.counts)) ``` ```text batch_id: uqt_d68a16dc528942b4b54b3dec68981af9 results: 3 0 {'00': 500, '11': 500} 1 {'00': 427, '01': 73, '10': 73, '11': 427} 2 {'00': 250, '01': 250, '10': 250, '11': 250} ``` 顺手笔算验证 i=1:线路是 H(0)、RX(1, π/4)、CNOT,末态为 $\frac{1}{\sqrt2}\left(\cos\frac{\pi}{8}(|00\rangle+|10\rangle) - i\sin\frac{\pi}{8}(|01\rangle+|11\rangle)\right)$,四个结果的概率为 $\frac{\cos^2(\pi/8)}{2} \approx 0.427$ 与 $\frac{\sin^2(\pi/8)}{2} \approx 0.073$——与 427/73 精确吻合。i=2 时 RX(π/2) 把 q[1] 变成均匀叠加,四个结果各 1/4。 批量下的单一句柄语义在所有平台一致:对支持原生批量的平台(OriginQ、IBM)线路会打包成一个平台任务;对 dummy 这类"批量能力为 1"的平台,uniqc 内部逐条循环执行,但你始终只管理一个 `uqt_*` 号。注意批次是"一荣俱荣、一损俱损":任何一条线路失败,整个批任务转为 `failed`,错误信息会指出出错的是第几个分片(shard)。 (uqt-cloud-config)= ## 6. 凭据与配置管理 所有用户状态集中在 `~/.uniqc/`,凭据在 `~/.uniqc/config.yaml`,按 **profile** 分组,`active_profile` 指明当前生效的组: ```yaml active_profile: default default: originq: token: "your-originq-token" ibm: token: "your-ibm-token" proxy: http: "http://proxy:8080" # 可选:内网访问 IBM 时需要 https: "http://proxy:8080" tianyan: login_key: "your-tianyan-login-key" logicalqubit: api_key: "your-logicalqubit-api-key" ``` 各平台凭证字段:OriginQ 用 `originq.token`,IBM 用 `ibm.token`(可选 `proxy`),天衍用 `tianyan.login_key`,LogicalQubit 用 `logicalqubit.api_key`,Quark 用 `quark.QUARK_API_KEY`(可选 `quark.host`)。 日常推荐用 CLI 配置(以下为**模板,本页未运行**,请替换成你自己的 token): ```bash uniqc config init # 生成默认配置文件 uniqc config set originq.token # 写入 token uniqc config set ibm.proxy.http http://proxy.example.com:8080 uniqc config validate # 校验配置格式 uniqc config profile list # 多账号隔离:列出/切换 profile uniqc config profile use work ``` 也可以临时用环境变量 `UNIQC_PROFILE=work` 切换 profile,或直接手动编辑 YAML 文件。 下面这段只读演示在**临时目录**里完整走一遍"写入配置 → 加载 → 校验",不触碰真实的 `~/.uniqc/config.yaml`(token 均为脱敏占位符): ```python import tempfile from pathlib import Path from uniqc.backend_adapter.config import load_config, save_config, validate_config workdir = Path(tempfile.mkdtemp(prefix="uniqc-config-demo-")) config_path = workdir / "config.yaml" save_config( { "active_profile": "demo", "demo": { "originq": {"token": "originq-token-redacted"}, "quark": {"QUARK_API_KEY": "quark-token-redacted"}, "ibm": { "token": "ibm-token-redacted", "proxy": {"http": "", "https": ""}, }, }, }, config_path=config_path, ) loaded = load_config(config_path=config_path) errors = validate_config(config_path=config_path) print("written to: /config.yaml") print("active profile:", loaded["active_profile"]) print("originq token (redacted):", loaded["demo"]["originq"]["token"]) print("validation errors:", errors) ``` ```text written to: /config.yaml active profile: demo originq token (redacted): originq-token-redacted validation errors: [] ``` 配置好 token 之后,先 `uniqc backend list -p originq` 拉取可用芯片列表(顺带填充本地芯片缓存),再挑一个 chip 提交——`provider:chip-name` 里的 chip 名就来自这个列表。 (uqt-cloud-cli)= ## 7. 命令行工作流:uniqc CLI `unified-quantum` 安装后自带 `uniqc` 命令。`uniqc --help` 列出全部子命令(节选): ```text ╭─ Commands ───────────────────────────────────────────────────────────────────╮ │ circuit Circuit format conversion (OriginIR <-> QASM) │ │ simulate Local circuit simulation │ │ submit Submit circuits to quantum cloud platforms │ │ result Query task results from quantum cloud platforms │ │ doctor Run diagnostics to verify your uniqc installation │ │ config Manage API key and configuration │ │ task Manage submitted tasks │ │ backend List, update, and inspect quantum cloud backends │ │ calibrate Run chip calibration experiments — XEB benchmarking │ ╰──────────────────────────────────────────────────────────────────────────────╯ ``` 命令行与 Python API 共享同一套任务缓存,所以 CLI 提交的任务能用 API 查,反之亦然。先看本机有哪些后端: ```bash uniqc backend list --platform dummy ``` ```text Available Backends ╭────────────┬──────────────────────────────┬──────────┬──────────────┬────────╮ │ Platform │ Name │ Qubits │ Status │ Type │ ├────────────┼──────────────────────────────┼──────────┼──────────────┼────────┤ │ dummy │ dummy:local:simulator │ - │ available │ sim │ │ dummy │ dummy:local:virtual-line-3 │ 3 │ available │ sim │ │ dummy │ dummy:local:virtual-grid-2x2 │ 4 │ available │ sim │ │ dummy │ dummy:local:mps-linear-3 │ 3 │ available │ sim │ ╰────────────┴──────────────────────────────┴──────────┴──────────────┴────────╯ ``` (输出还会列出本机各平台的芯片缓存状态;`virtual:*` 开头的条目是用户自定义噪声虚拟机,见[第 3 章](../ch03-simulation/index.md)。) 把 Bell 线路存成 OriginIR 文件 `bell.originir`,先本地模拟、再提交: ```text QINIT 2 CREG 2 H q[0] CNOT q[0], q[1] MEASURE q[0], c[0] MEASURE q[1], c[1] ``` ```bash uniqc simulate bell.originir --shots 256 ``` ```text Simulation Results ┏━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━┓ ┃ State ┃ Count ┃ Probability ┃ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━┩ │ 00 │ 127 │ 50.0% │ │ 11 │ 127 │ 50.0% │ └───────┴───────┴─────────────┘ ``` (`simulate` 走随机采样,计数每次运行略有不同。) 提交并等待结果,`--format json` 方便脚本消费: ```bash uniqc submit bell.originir --backend dummy -s 64 --wait --format json ``` ```json { "task_id": "uqt_19391564e0294b108980e7e1c124997e", "backend": "dummy:local:simulator", "shots": 64 } { "counts": { "00": 32, "11": 32 }, "probabilities": { "00": 0.5, "11": 0.5 }, "shots": 64, "platform": "dummy", "task_id": "uqt_19391564e0294b108980e7e1c124997e", "backend_name": "dummy:local:simulator", "execution_time": null, "error_message": null } ``` 第一个 JSON 是提交回执,第二个是 `--wait` 等到的结果。之后随时可以凭 task_id 回查或管理: ```bash uniqc result uqt_19391564e0294b108980e7e1c124997e uniqc task list --limit 5 ``` ```text Result for uqt_19391564e0294b108980e7e1c12 4997e ┏━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━┓ ┃ State ┃ Count ┃ Probability ┃ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━┩ │ 00 │ 32 │ 50.0% │ │ 11 │ 32 │ 50.0% │ └───────┴───────┴─────────────┘ ``` ```text Tasks ┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓ ┃ Task ID ┃ Platform ┃ Status ┃ Shots ┃ Submit Time ┃ ┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩ │ uqt_19391564e029… │ dummy:local:simu… │ success │ 64 │ 2026-09-05T03:30:… │ └───────────────────┴───────────────────┴─────────┴───────┴─────────────────────┘ ``` (`task list` 的行数与内容取决于本机任务历史,每次运行略有不同;此处只保留了一行。) 提交前想确认线路与目标后端兼容,`--dry-run` 只做本地翻译与兼容性检查、不发任何网络请求: ```bash uniqc submit bell.originir --backend dummy --dry-run ``` ```text ✓ [DRY-RUN PASSED] Dry-run passed for dummy simulator: OriginIR is valid. Qubits=2, shots=1000 Circuit qubits: 2 Backend: dummy:local:simulator ``` 真机路径同样是换 `--backend`(以下为**模板,本页未运行**,需要先 `uniqc config set originq.token `): ```bash uniqc submit bell.originir --backend originq:WK_C180 --shots 1024 --wait ``` ## 下一步 - 线路在真机上跑歪了怎么办?继续[第 6 章:芯片校准与误差缓解](../ch06-calibration-qem/index.md),用 XEB 与 M3 把噪声"量出来、除回去" - 想看统一结果如何直接变成图表,见[第 4 章:可视化](../ch04-visualization/index.md) - 期望值后处理是变分算法的地基,实战见[第 7 章:VQE 实操](../ch07-labs/index.md);理论推导读[量子计算算法教程·VQE](https://chenzhaoyun.com/quantum-tutorial/ch08-qml/vqe-tutorial.html) ## 练习题 **练习 1【云执行工作流】**(→ [第 1 节](#uqt-cloud-workflow)) 1. 把第 1 节线路里的 `cnot(0, 1)` 改成 `cz(0, 1)`,先笔算测量分布是否变化(注意 CZ 只给 |11⟩ 加相位),再运行验证你的预测。 2. 提交任务后不调用 `wait_for_result`,直接 `query_task(task_id)` 打印 `info.status` 与 `info.task_id`,确认与 `UnifiedResult` 携带的 `task_id` 一致,并解释为什么 task_id 是任务的唯一句柄。 **练习 2【后端命名与平台选择】**(→ [第 2 节](#uqt-cloud-backends)) 1. 把第 2 节脚本中的 `virtual-line-3` 换成 `virtual-grid-2x2`,预测 Bell 线路能否成功、计数是多少,再运行验证。 2. 解释 `submit_task(circuit, backend="originq")` 为什么会失败;正确的写法是什么,可用 chip 列表从哪里获得? > 提示:backend 需要 `provider:chip-name` 规范格式,错误信息会列出本地缓存里可用的 chip。 **练习 3【异步轮询与任务状态】**(→ [第 3 节](#uqt-cloud-polling)) 1. 把第 3 节轮询循环里的 `time.sleep(0.5)` 改成 `time.sleep(0.1)`,观察 dummy 后端下循环体执行了几次,并解释在真实云平台上这个循环为什么通常会转很多圈。 2. 构造一个必定失败的任务(参考拓扑违规线路),先用 `poll_result` 确认状态为 `failed`,再用 `wait_for_result(task_id, raise_on_failure=False)` 调用一次,观察返回值是什么。 **练习 4【统一结果与期望值后处理】**(→ [第 4 节](#uqt-cloud-result)) 1. 删掉第 4 节线路里的 `circuit.x(0)`,先笔算新的 counts 支集与 ⟨IIZ⟩ 的值,再运行验证。 2. 先预测 `calculate_expectation(result.probabilities, 'IZI')` 的值(q[1] 恒为 |1⟩),再运行验证;随后把测量顺序改成 `circuit.measure(2, 1, 0)`,解释 counts 键为什么变了。 > 提示:Pauli 串与 bitstring 同序,c[i] 记录第 i 次 measure 的结果,与 qubit 编号无关。 **练习 5【批量提交】**(→ [第 5 节](#uqt-cloud-batch)) 1. 把第 5 节的 `range(3)` 改成 `range(5)`,先笔算 i=3、i=4 两个线路(RX 角分别为 3π/4 与 π)的计数分布,再运行验证。 2. 在批次中间插入一条拓扑违规线路(`cnot(0, 2)` 提交到 `virtual-line-3`),预测整批任务的行为再运行验证。 > 提示:批次一损俱损,错误信息里的 shard 编号指出出错的是第几条线路。 **练习 6【凭据与配置管理】**(→ [第 6 节](#uqt-cloud-config)) 1. 把第 6 节只读演示里的 profile 名从 `demo` 改成 `prod`,运行并观察 `load_config` 返回结构与 `active_profile` 字段的变化。 2. 运行 `uniqc config profile --help`,写出列出与切换 profile 的命令,并解释多 profile 适合什么场景。 > 提示:环境变量 `UNIQC_PROFILE` 可以临时切换 profile,不改动配置文件。 **练习 7【命令行工作流】**(→ [第 7 节](#uqt-cloud-cli)) 1. 写一个三比特 GHZ 线路的 OriginIR 文件(H、CNOT(0,1)、CNOT(0,2)、测量),先写出你预期 `uniqc simulate --shots 256` 的计数表,再运行对比(注意 simulate 是随机采样)。 2. 把 `uniqc submit bell.originir --backend dummy` 的 `--shots` 改成 10,预测 `--wait --format json` 输出的 counts 是否仍精确等于 5/5,运行验证并解释。 > 提示:dummy 后端按精确概率确定性生成计数,10 × 0.5 = 5。