# Qiskit 使用示范

用你熟悉的 QuantumCircuit 编写线路，再通过 QuantumFerry 选择设备、取得报价并读取 Qiskit 结果。插件提供 BackendV2、SamplerV2 和 EstimatorV2 三种入口：测量线路直接用 backend，参数化采样用 Sampler，观测量期望值用 Estimator。

| 组件 | 用它完成什么 | 本页的入口 |
| --- | --- | --- |
| QuantumSourceService | 连接平台、查找设备、创建计算对象 | QuantumSourceService(...) |
| QuantumSourceBackend | 把 QuantumCircuit 准备为平台任务，返回 Qiskit Result | service.return_backend(...) |
| QuantumSourceSampler | 为参数化线路采样，按经典寄存器读取 BitArray | service.create_sampler(backend=backend) |
| QuantumSourceEstimator | 将 Pauli 观测量转换为测量实验，计算期望值和误差 | service.create_estimator(backend=backend) |

- [尚未安装：安装 Qiskit 扩展](install.html)
- [准备平台连接与认证](connection.html)
- [已会普通线路：Sampler 与 Estimator 完整教程](qiskit-primitives.html)

## 选择你的学习路径

| 你现在想做什么 | 阅读顺序 |
| --- | --- |
| 第一次提交测量线路 | 本页第 1–5 步：连接 → 线路 → 报价 → 确认 → counts |
| 查看设备门集、物理连接或 mapping 等选项 | Qiskit 设备能力页：目录 → capabilities / schema / Options / Target |
| 固定物理位置 | 编译与物理比特页：查询当前连接，再选择布局 |
| 参数扫描、批量采样、观测量估计 | Sampler 与 Estimator 页：PUB → 参数数组 → 完整报价 → 原生结果 |

本页先完成一个 Bell 实验：连接、选设备、写线路、看报价、确认、读结果。prepare 上传线路并获取报价，confirm 确认执行。后面的直接运行、批量、Sampler、Estimator 分别介绍其他使用方式，可按需要选择。确认真机实验会使用设备资源，并按报价计费。

## 1. 创建服务并选择设备

开始前完成安装与认证：安装 SDK 的 Qiskit 扩展，设置 QUANTUM_SOURCE_API_URL，并配置平台 SDK Key 文件或 Client ID / Client Secret。QUANTUM_SOURCE_BACKEND 填写设备列表返回的 short_code。下面各步骤在同一个 Python 会话中运行，共用 service、backend 和线路变量。

```python
import os
from qiskit import QuantumCircuit
from quantum_source_qiskit import QuantumSourceService

service = QuantumSourceService(
    base_url=os.environ["QUANTUM_SOURCE_API_URL"],
    currency="OPEN",  # 使用 FULL 时在首次准备前改为 "FULL"
)
devices = service.backends(online=True)
devices
```

连接单元使用 OPEN，省略 currency 时也默认 OPEN。需要使用 FULL 余额时，在准备之前将这里改为 currency="FULL"。service.currency 和 service.scheduler.currency 可只读查看当前选择；由 service 创建的 Backend、Sampler、Estimator 和批次都使用这个钱包。

QuantumSourceService 负责平台连接。backends(online=True) 返回设备字典列表，在 Notebook 中显示简洁表格；short_code 是下一步选择设备时使用的标识。platform_active_tasks 显示全平台在该设备上的未结束任务总数。

```python
backend = service.return_backend(os.environ["QUANTUM_SOURCE_BACKEND"])
backend.available_qubits, backend.max_shots
```

return_backend 返回实现 Qiskit BackendV2 接口的 QuantumSourceBackend。backend 保存所选设备的能力和默认选项，后面的 prepare、run 和 transpile 都针对这台设备。available_qubits 是可用物理编号集合，max_shots 是设备公布的采样上限。

- [更多筛选、门集、Target 与设备选项](capabilities-qiskit.html)

## 2. 创建一个逻辑线路

新建线路默认采用逻辑线路方式：SDK 转换输入门，设备端完成后续物理编译。本源设备通过 mapping 开启云端拓扑映射，将逻辑线路适配芯片连接关系。下面使用两个逻辑比特，采样 128 次。

```python
circuit = QuantumCircuit(2, 2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure([0, 1], [0, 1])
circuit.draw("text")
```

QuantumCircuit(2, 2) 创建两条量子线和两位经典结果。h(0) 和 cx(0, 1) 构造 Bell 线路，measure 将测量写入经典位。这里的 0、1 是逻辑线路编号；物理位置由设备映射决定。draw("text") 在本地显示线路。

## 3. 准备线路并查看报价

```python
prepared = backend.prepare(circuit, shots=128)
prepared.quote
```

prepare 返回保存线路和报价的准备对象 prepared。shots=128 表示这个实验采样 128 次；插件完成线路转换和平台准备后，把任务数量、采样次数和价格放在 prepared.quote 中，供确认执行前查看。

| 本例参数 | 省略时 | 覆盖范围 |
| --- | --- | --- |
| circuit | 必填的位置参数；也可传线路列表 | 本次准备的线路 |
| shots=128 | 采用 backend.options.shots，初始为 100 | 每条线路各自的采样次数 |
| memory | 采用 backend.options.memory，初始为 True | 请求设备提供的逐次测量数据；只读取 counts 时可设为 False |
| export_format | 按当前设备能力选择 qasm3 或 qasm2 | 线路序列化使用的 OpenQASM 版本 |

| 报价字段 | 怎样读取 |
| --- | --- |
| backends | 本次准备所涉及的设备 |
| task_count / total_shots | 实际任务数及所有任务采样次数之和 |
| total_amount / currency | 精确 Decimal 金额及币种；amount 提供字符串金额 |
| expires_at_utc | 这份报价的到期时间 |

```python
prepared.quote.to_dict()
```

to_dict() 将报价转换为适合展示或保存的字典。

金额和币种在 prepare 时确定，OPEN 与 FULL 分别记账。需要改用 FULL 时，用 FULL service 创建 backend，再为尚未执行的实验准备新报价。已有 prepared 保留它原来的金额和币种。

## 4. 核对后单独确认

接受上述报价后，运行下一单元确认执行。SDK 使用 prepared 保存的完整报价提交实验；真机任务按报价计费。

```python
job = prepared.confirm()
```

confirm 使用 prepared 保存的线路和报价确认提交，返回 job。job 用于查询实验进度，并在计算完成后读取结果。

## 5. 等待并读取测量计数

```python
result = job.result(timeout=300)
result.get_counts()
```

result(timeout=300) 等待实验结果，为本次调用设置 300 秒等待预算，完成后返回 Qiskit Result。get_counts() 读取测量计数字典：键是比特串，值是对应次数，各计数之和与结果中的 shots 一致。

Qiskit 的结果位序按经典寄存器解释：本例左侧字符对应 c[1]，右侧对应 c[0]。即使将线路固定在物理 1、11 上，结果仍使用这两位经典寄存器。

get_counts() 读取平台的整数计数通道；设备提供逐次测量数据时还可使用 get_memory()。job.status() 查询当前进度，job.platform_job_ids 列出已经取得的任务号。排队阶段可使用句柄的 submission_ids 跟踪提交；job_id() 在任务号分配前返回 None。

## 替代方式：直接运行

run 将准备和确认合并为一步，适合程序已经决定执行的实验。它默认自动批准服务端报价并提交；需要逐次查看报价时，使用前面的 prepare → quote → confirm 流程。

```python
job = backend.run(circuit, shots=128)
result = job.result(timeout=300)
result.get_counts()
```

| 希望怎样使用 | 选哪个入口 |
| --- | --- |
| 先显示完整价格，再由程序或用户决定 | backend.prepare(...) → prepared.quote → prepared.confirm() |
| 已接受当前计费规则，直接执行 | backend.run(...)，默认自动确认 |
| 保留 run 调用形式，但要求显式审价 | run(..., auto_approve_quote=False)，处理 QuoteApprovalRequired.prepared |

可在 service.return_backend(..., config={"auto_approve_quote": False}) 设置后续默认不自动确认；也可在单次 run 中传 auto_approve_quote=False。此时抛出 QuoteApprovalRequired，其 prepared 属性保留准备好的报价。Sampler 和 Estimator 继承后端的这一设置。

## 可选：等待时间、查询间隔与进度

Backend 的 prepare/run 可以设置 verbose 和 job_timeout_seconds，分别控制进度显示和默认等待时间。下面为第 3 步的准备调用增加这两个选项。已有任务的等待时间可以直接通过 job.result(timeout=...) 调整。

```python
prepared = backend.prepare(
    circuit,
    shots=128,
    verbose=True,
    job_timeout_seconds=300,
)
prepared.quote
```

verbose=True 显示简短英文阶段或状态变化，job_timeout_seconds=300 为后续结果等待设置默认预算。prepare 准备报价，confirm 确认执行；backend.run(...) 合并这两步并返回任务句柄，随后用 job.result() 等待计算结果。

先按第 4 步查看报价并确认取得 job，再选择下面一种结果读取写法。

```python
# 已经取得同一实验的 job：采用提交时设置的默认等待预算。
result = job.result(wait=2)
result.get_counts()
```

```python
# 或显式设置本次等待时间，继续读取同一个 job。
result = job.result(timeout=600, wait=2)
result.get_counts()
```

| 参数 | 设置位置 | 含义 |
| --- | --- | --- |
| verbose | backend.prepare/run | 默认 False；True 显示准备、提交及后续等待的简短进度。 |
| job_timeout_seconds | backend.prepare/run | 作为后续 result() 的默认等待秒数；从 result() 调用时开始计算。 |
| timeout | job.result(timeout=...) | 本次调用使用的等待秒数。 |
| wait | job.result(wait=2) | 状态查询间隔，覆盖平台排队和设备任务等待。 |

省略 wait 时，平台排队阶段使用 scheduler 的查询间隔，设备任务阶段使用 2 秒。一次 result 调用中的排队和全部线路共享同一份等待预算；恢复 prepared 得到的任务句柄沿用这些设置。单次 HTTP 请求的超时由连接层控制。

等待超时后，任务仍会继续处理。再次调用原 job.result(...) 即可继续等待和读取结果。本节介绍 Backend 的等待设置；Sampler、Estimator 的用法见原语教程，PennyLane 的用法见对应插件教程。

## 设备选项与固定物理位置

选择设备后，查看 backend.configuration_schema 中的参数说明和 backend.options 中的当前默认值。

```python
backend.configuration_schema, backend.options
```

configuration_schema 的 properties 给出设备选项的类型、允许值和默认值，例如本源设备的 amend、mapping、optimization。有默认值的选项进入 backend.options，可用 set_options 设置后续默认；单次 prepare/run 可通过直接关键字或扁平 configuration_data 设置。

固定位置时，选择 physical_qubits 或 initial_layout 中的一个入口，按逻辑线顺序填写物理编号。先从当前设备的可用编号及直接连接中选位；也可先 transpile 生成布局，再直接提交带布局的线路。完整步骤见编译与物理比特。

- [完整查询教程：Qiskit 的 schema、Options、能力与 Target](capabilities-qiskit.html)
- [编译与物理比特：动态选项、优先级与两种布局入口](compilation.html)

## 多线路与完整报价

以下是独立的多线路实验示例，使用前面定义的 circuit 和 backend。shots=128 对每条线路取同一值，也可传与线路数相同的正整数列表。先看整个批次的实际任务数、shots 和金额，再决定是否确认。

```python
batch = backend.prepare([circuit, circuit.copy()], shots=[128, 128])
batch.quote
```

这里输入两条线路，分别采样 128 次。返回的批次准备对象通过 quote 展示两条线路的总报价。确认该批次后，按原线路顺序读取结果。

```python
batch_job = batch.confirm()
```

```python
batch_result = batch_job.result(timeout=300)
batch_result.get_counts(0), batch_result.get_counts(1)
```

每条实际任务分别占用组织名额。如果批量确认中途停止，已接受的任务继续处理；保留 batch，使用 recover() 查看已尝试提交的进度。

## Sampler：参数绑定与采样结果

- [完整教程：PUB、shots 优先级、经典寄存器与后台结果等待](qiskit-primitives.html#sampler)

这是独立的 Sampler 实验，沿用本页连接阶段的 service 和 backend。准备阶段会绑定参数并取得所有实际任务的报价；确认和结果分开运行。

```python
from qiskit.circuit import Parameter

theta = Parameter("theta")
sample_circuit = QuantumCircuit(1, 1)
sample_circuit.ry(theta, 0)
sample_circuit.measure(0, 0)
sampler = service.create_sampler(backend=backend)
sample_pubs = [(sample_circuit, [[0.0], [0.5]], 128)]
sample_prepared = sampler.prepare(sample_pubs)
sample_prepared.quote
```

sample_pubs 是一组 PUB 输入；本例的一个 PUB 由线路、参数值数组、shots 组成。[[0.0], [0.5]] 为 theta 提供两个参数点，每点采样 128 次，共 256 shots。

```python
sample_job = sample_prepared.confirm()
```

```python
sample_result = sample_job.result(timeout=300)
sample_result[0].data.c.get_counts(0)
```

sample_result[0] 选择第一个 PUB，data.c 选择名为 c 的经典寄存器，get_counts(0) 读取第一个参数点，get_counts(1) 读取第二点。

PUB 中的 shots 优先于通用默认值；工厂参数 options={"default_shots": 128} 可设置默认采样次数。经典寄存器保留在同名 data 字段中，BitArray 按平台返回的 counts 展开。sampler.run(sample_pubs) 可直接准备、自动确认并执行实验。

## Estimator：期望值与测量任务

- [完整教程：precision、测量分组与多观测量广播](qiskit-primitives.html#estimator)

这是另一个独立实验，沿用本页连接阶段的 service 和 backend。下例使用单个已绑定线路和 Z 观测量，precision=0.1 对每个实际测量线路采用 100 shots；若设备有更严格限制，请先调整精度。

```python
from qiskit.quantum_info import SparsePauliOp

state_circuit = QuantumCircuit(1)
state_circuit.ry(0.5, 0)
observable = SparsePauliOp.from_list([("Z", 1.0)])
estimator = service.create_estimator(backend=backend)
estimate_prepared = estimator.prepare([(state_circuit, observable)], precision=0.1)
estimate_prepared.quote
```

Estimator 输入不需要事先添加最终测量。这里的 PUB 是 (线路, 观测量)：SparsePauliOp 中 Z 表示测量 Z 的期望值，系数为 1.0。prepare 会安排所需测量线路，报价反映展开后的实际数量。

```python
estimate_job = estimate_prepared.confirm()
```

```python
estimate_result = estimate_job.result(timeout=300)
estimate_result[0].data.evs, estimate_result[0].data.stds
```

evs 是观测量期望值，stds 是对应估计标准误。可以将实际估计与 Ry(0.5) 后测 Z 的理论值 cos(0.5) 比较。

Estimator 在客户端完成参数绑定、Pauli 测量分组和结果归约。每个实际测量线路的 shots 为 ceil(1 / precision**2)，因此一个参数点可能展开为多个任务。以准备后的完整报价为准。Sampler、Estimator 的 prepare/run 均接受 physical_qubits 或 initial_layout，以及当前设备声明的选项。

Primitive 确认后在本机后台整理结果。primitive_job.result(timeout=300) 为本次等待设置 300 秒时限，超时后可用原句柄继续等待。读取结果期间保持内核和 service 可用；跨会话读取平台任务结果的方式见专页的等待与恢复。

## 状态查询、取消与组织名额

```python
job.status(), getattr(job, "submission_ids", ()), job.platform_job_ids
```

依次查看状态、句柄可提供的原提交 ID、已取得的真实任务 ID。排队句柄提供 submission_ids；直接返回真实任务时，中间项为空元组。

平台排队中的任务可继续用 result(timeout=...) 等待。取消尚未被平台领取的 queued 项时调用 job.cancel()。全部取消成功返回 True；单任务失败抛对应异常，多任务部分失败抛 BatchCancellationError，已成功取消的部分保持生效。

同一组织的成员、Key、项目与设备共享 10 个未结束任务名额。平台接受确认后占用名额，确认 Completed、Failed 或 Canceled 后释放。达到上限时，JobLimitReachedError 提示查看已有任务，待名额可用后再提交新的实验。

## 简短报错与原任务恢复

提交或查询报错时，阅读异常消息并保留 prepared 或 job，随后查询同一个实验。已经取得 job 时调用它的 status() 或 result(timeout=...)；提交结果待核实时调用 prepared.recover()。recover() 使用原提交信息，返回可等待任务句柄后即可继续调用 result(timeout=...)。仍返回回执时，稍后再次 recover()。等待超时后，任务继续保留，重新调用原 job.result(...) 即可继续等待。

SDK 通过简短的异常类型和消息显示可用的提交原因。平台确认任务最终 Failed 后，按原币种退还该任务费用。

## 配置入口速查

设备选项统一写入扁平的 configuration_data，或使用当前设备声明的单次关键字。钱包在 QuantumSourceService / SchedulerClient 构造时通过 currency="OPEN" 或 "FULL" 选择。prepared.quote.currency 可查看已准备实验的币种；换用新客户端后，已有 prepared 保留原报价。

SDK 默认使用 OPEN，在 service 构造时可选择 FULL。APIError（HTTP 402）表示所选钱包余额不足，本次确认项停留在提交前；批次此前接受的任务继续处理。当前通道为 public / stand，执行方案 auto 对应 public，队列 auto 或 standard 对应 stand。完成结果读取后关闭 service，任务记录仍保存在平台。

```python
service.close()
```

## 下一步

- [下载逻辑线路 Notebook（未执行）](../../notebooks/sdk_qiskit_current.ipynb)
- [下载固定物理位置 Notebook（未执行）](../../notebooks/sdk_qiskit_physical_current.ipynb)
- [Sampler 与 Estimator 完整教程](qiskit-primitives.html)
- [常用 API](api.html)

## 下载本页 Jupyter Notebook

收录本页 Python 示例与说明，便于在 Jupyter Notebook 中学习和运行；安装命令保留为说明。

[下载《Qiskit 入门与示例》Notebook（.ipynb）](../../notebooks/sdk_user_guide_qiskit.ipynb)
