云执行:把线路交给量子平台

本地模拟回答"线路对不对",云执行回答"线路在真实平台上跑出来是什么样"。本章走通 UnifiedQuantum 的远端任务主链路:submit_task 提交 → wait_for_result 取结果 → query_task 查状态,讲清后端命名规则、异步轮询、统一结果的后处理、批量提交、凭据配置,最后用 uniqc 命令行把整套流程串起来。

本页示例基于 unified-quantum 0.1.0,所有输出均为实际运行结果。本章约定:可运行的示例全部跑在 dummy 后端家族上(不需要任何 token、不联网);涉及真实云平台的代码以"模板"标注呈现,需要你先配置平台凭据(见第 6 节)才能运行。

测量统计与期望值的理论背景见量子计算算法教程·量子计算基础。如果你还没读过快速开始第 5 节的统一执行入口,建议先补上。

本课知识点

  1. 云执行工作流——能写出 submit_task wait_for_result query_task 的完整调用链,并解释每一步的返回值。

  2. 后端命名与平台选择——能解释 provider:chip-name 规范格式与 dummy 家族各成员的语义,为给定线路选择合适的后端。

  3. 异步轮询与任务状态——能用 poll_resultTaskStatus 构造非阻塞轮询循环,并正确处理失败任务。

  4. 统一结果与期望值后处理——能从 UnifiedResult 读取计数并计算 Pauli 期望值,解释 bitstring 的 LSB 约定。

  5. 批量提交——能用 submit_batch 一次提交整组线路,并按提交顺序收集各线路的结果。

  6. 凭据与配置管理——能写出配置各平台 token 的命令,描述 ~/.uniqc/config.yaml 的 profile 结构。

  7. 命令行工作流——能用 uniqc submit/result/task 完成"提交—取结果—管理"的命令行闭环。

1. 云执行工作流:submit → wait → query

三个函数构成主链路:submit_task 把线路交给后端并立即返回任务号(uqt_ 开头);wait_for_result 阻塞等待任务完成并返回统一结果;query_task 查询任务的元信息。先用最熟悉的 Bell 线路在 dummy 后端上走一遍:

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)
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):

# 模板:提交到 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)

2. 后端命名与平台选择

backend 参数是字符串,两种形态:

  • 云平台后端provider:chip-name 规范格式,如 originq:WK_C180ibm:ibm_brisbane。仅传裸平台名('originq')会被拒绝,错误信息会列出本地缓存里可用的 chip。

  • 本地 dummy 后端:以 dummy 开头,不联网、不需要凭据,是本章的主角。

后端 ID

含义

dummydummy:local:simulator

无约束、无噪声的本地虚拟机

dummy:local:virtual-line-N

N 比特线性拓扑(链),无噪声

dummy:local:virtual-grid-RxC

R×C 网格拓扑,无噪声

dummy:local:mps-linear-N

N 比特 MPS 模拟器(线性链)

dummy:<platform>:<backend>

复用真实芯片的拓扑与标定数据,本地含噪模拟(规则型写法,不在后端列表中)

originq:WK_C180

OriginQ 180 比特真机(需 token)

originq:full_amplitude

OriginQ 全振幅云模拟器(需 token)

ibm:ibm_brisbane

IBM 真机(需 token)

同一份代码换后端即可迁移。dummy 家族内部对比:

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)}")
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-3simulator 的区别在于它强制执行拓扑约束——双比特门只能落在相邻比特对上,第 3 节会看到违反约束的下场。

各平台定位速览:

平台

定位

额外依赖

OriginQ Cloud

主生产路径

IBM Quantum

国际平台

无(qiskit 是核心依赖)

Tianyan(天衍)

国内平台(QCIS 格式)

LogicalQubit

逻辑比特平台

QuarkStudio / Quark

第三方云平台

pip install unified-quantum[quark](Python ≥ 3.12)

Dummy

本地模拟

dummy:<platform>:<backend>(如 dummy:originq:WK_C180)是一类特别的成员:先用真实芯片的拓扑与标定数据做编译,再在本地注入该芯片的噪声执行,让你在花真机额度之前预估线路在该芯片上的表现。它需要本地已有对应平台的芯片缓存(配置 token 后运行 uniqc backend list -p originq 会自动拉取),含噪模拟的细节见第 3 章

3. 异步轮询与任务状态

任务有五种状态:

TaskStatus

含义

PENDING

已登记、尚未开始

RUNNING

执行中

SUCCESS

成功完成

FAILED

执行失败

CANCELLED

已取消

poll_result非阻塞查询:立即返回当前状态,适合放进自己的循环里;wait_for_resultget_result 是阻塞接口,内部替你轮询到终态:

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))
poll: success
final: success
{'00': 512, '11': 512}

注意第一次 poll_result 就返回 success——dummy 在提交时就同步算完了。真实云平台上这段循环会先看到 running、转若干圈后到 success,这正是轮询模式存在的意义:你可以在等待间隙做别的事(提交下一批线路、写文件)。

再看失败路径。把一个双比特门放到 virtual-line-3 的不相邻比特对上:

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], "…")
poll: failed
TaskFailedError: Task 'uqt_3e43e1439c4d4c85b232946256924267' failed on backend 'dummy:local:virtual-line-3': shard 0: Error in  …

拓扑违规不会在提交时被拦下,而是在执行时失败:poll_result 给出 failedwait_for_result 默认抛 TaskFailedError(传 raise_on_failure=False 则返回 None,适合批量任务"先收尸、后分析");等超时则抛 TaskTimeoutError。错误信息里带着后端名、分片号与出错的门,足以定位问题。

所有任务(含结果与元数据)都落在本地缓存 ~/.uniqc/cache/tasks.sqlite 里,进程退出、机器重启都不丢:

from uniqc import list_tasks

for t in list_tasks(limit=3):
    print(t.task_id[:16] + "…", t.status, t.backend)
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()

4. 统一结果与期望值后处理

各平台原生结果格式五花八门(IBM 的小端 BitArray、天衍的测量比特标签序……),UnifiedResult 把它们全部归一:counts{bitstring: 计数}probabilities 是归一化概率,另有 shotsplatformbackend_nametask_idexecution_timeerror_message 等元信息字段。

先跑一个三比特线路,再做两类后处理:

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"<ZII> = {calculate_expectation(result.probabilities, 'ZII'):+.4f}")
print(f"<IIZ> = {calculate_expectation(result.probabilities, 'IIZ'):+.4f}")

print("shots2prob:", {k: round(float(v), 4) for k, v in shots2prob(result.counts).items()})
counts: {'001': 0, '011': 500, '101': 0, '111': 500}
probabilities: {'001': 0.0, '011': 0.5, '101': 0.0, '111': 0.5}
<ZII> = +0.0000
<IIZ> = -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'——

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))
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与本站第 7 章实操

0 计数的键。 counts 里出现了 '001''101' 两个 0 计数键:RX(math.pi) 中浮点数 π 让 cos(π/2) ≈ 6×10⁻¹⁷ ≠ 0,q[1]=0 的分支留下了微小振幅,态矢量模拟器便把它计入支集。真实硬件只返回实际测到的结果,通常不会出现 0 计数键;后处理代码不应假设键集合等于支集。

5. 批量提交:submit_batch

参数扫描、变分算法的每个迭代步都要提交一组线路。submit_batch 接受线路列表,返回一个 uqt_* 批任务号;wait_for_result 对批任务返回 list[UnifiedResult],顺序与提交顺序一致:

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))
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)。

6. 凭据与配置管理

所有用户状态集中在 ~/.uniqc/,凭据在 ~/.uniqc/config.yaml,按 profile 分组,active_profile 指明当前生效的组:

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):

uniqc config init                              # 生成默认配置文件
uniqc config set originq.token <YOUR_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 均为脱敏占位符):

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: <temporary-directory>/config.yaml")
print("active profile:", loaded["active_profile"])
print("originq token (redacted):", loaded["demo"]["originq"]["token"])
print("validation errors:", errors)
written to: <temporary-directory>/config.yaml
active profile: demo
originq token (redacted): originq-token-redacted
validation errors: []

配置好 token 之后,先 uniqc backend list -p originq 拉取可用芯片列表(顺带填充本地芯片缓存),再挑一个 chip 提交——provider:chip-name 里的 chip 名就来自这个列表。

7. 命令行工作流:uniqc CLI

unified-quantum 安装后自带 uniqc 命令。uniqc --help 列出全部子命令(节选):

╭─ 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 查,反之亦然。先看本机有哪些后端:

uniqc backend list --platform dummy
                               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 章。)

把 Bell 线路存成 OriginIR 文件 bell.originir,先本地模拟、再提交:

QINIT 2
CREG 2
H q[0]
CNOT q[0], q[1]
MEASURE q[0], c[0]
MEASURE q[1], c[1]
uniqc simulate bell.originir --shots 256
      Simulation Results
┏━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━┓
┃ State ┃ Count ┃ Probability ┃
┡━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━┩
│ 00    │ 127   │ 50.0%       │
│ 11    │ 127   │ 50.0%       │
└───────┴───────┴─────────────┘

simulate 走随机采样,计数每次运行略有不同。)

提交并等待结果,--format json 方便脚本消费:

uniqc submit bell.originir --backend dummy -s 64 --wait --format 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 回查或管理:

uniqc result uqt_19391564e0294b108980e7e1c124997e
uniqc task list --limit 5
          Result for
uqt_19391564e0294b108980e7e1c12
             4997e
┏━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━┓
┃ State ┃ Count ┃ Probability ┃
┡━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━┩
│ 00    │ 32    │ 50.0%       │
│ 11    │ 32    │ 50.0%       │
└───────┴───────┴─────────────┘
                                     Tasks
┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓
┃ Task ID           ┃ Platform          ┃ Status  ┃ Shots ┃ Submit Time        ┃
┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩
│ uqt_19391564e029… │ dummy:local:simu… │ success │ 64    │ 2026-09-05T03:30:… │
└───────────────────┴───────────────────┴─────────┴───────┴─────────────────────┘

task list 的行数与内容取决于本机任务历史,每次运行略有不同;此处只保留了一行。)

提交前想确认线路与目标后端兼容,--dry-run 只做本地翻译与兼容性检查、不发任何网络请求:

uniqc submit bell.originir --backend dummy --dry-run
✓ [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 <YOUR_TOKEN>):

uniqc submit bell.originir --backend originq:WK_C180 --shots 1024 --wait

下一步

练习题

练习 1【云执行工作流】(→ 第 1 节

  1. 把第 1 节线路里的 cnot(0, 1) 改成 cz(0, 1),先笔算测量分布是否变化(注意 CZ 只给 |11⟩ 加相位),再运行验证你的预测。

  2. 提交任务后不调用 wait_for_result,直接 query_task(task_id) 打印 info.statusinfo.task_id,确认与 UnifiedResult 携带的 task_id 一致,并解释为什么 task_id 是任务的唯一句柄。

练习 2【后端命名与平台选择】(→ 第 2 节

  1. 把第 2 节脚本中的 virtual-line-3 换成 virtual-grid-2x2,预测 Bell 线路能否成功、计数是多少,再运行验证。

  2. 解释 submit_task(circuit, backend="originq") 为什么会失败;正确的写法是什么,可用 chip 列表从哪里获得?

提示:backend 需要 provider:chip-name 规范格式,错误信息会列出本地缓存里可用的 chip。

练习 3【异步轮询与任务状态】(→ 第 3 节

  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 节

  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 节

  1. 把第 5 节的 range(3) 改成 range(5),先笔算 i=3、i=4 两个线路(RX 角分别为 3π/4 与 π)的计数分布,再运行验证。

  2. 在批次中间插入一条拓扑违规线路(cnot(0, 2) 提交到 virtual-line-3),预测整批任务的行为再运行验证。

提示:批次一损俱损,错误信息里的 shard 编号指出出错的是第几条线路。

练习 6【凭据与配置管理】(→ 第 6 节

  1. 把第 6 节只读演示里的 profile 名从 demo 改成 prod,运行并观察 load_config 返回结构与 active_profile 字段的变化。

  2. 运行 uniqc config profile --help,写出列出与切换 profile 的命令,并解释多 profile 适合什么场景。

提示:环境变量 UNIQC_PROFILE 可以临时切换 profile,不改动配置文件。

练习 7【命令行工作流】(→ 第 7 节

  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。