# Core 使用示范

使用 Core SDK 提交自己的 OpenQASM 线路，完成设备查询、任务报价、提交和结果下载。本页从一个 Bell 实验开始，再介绍文件输入、设备配置、批量实验和结果查询。

## Core SDK 提供什么

| 对象 | 用途 | 本页中的变量 |
| --- | --- | --- |
| SchedulerClient | 连接平台，准备和提交实验，查询任务并下载结果。 | scheduler |
| scheduler.management | 浏览设备目录、厂商和账号可见的组织。 | 由 scheduler 持有的管理客户端 |
| JobSubmissionConfig | 描述一次任务使用的设备、采样次数和设备选项。 | config |
| PreparedSubmission | 保留已经准备好的线路和报价，供确认或查询原提交。 | prepared |
| UnifiedResult | 提供统一的主要结果通道及其类型。 | result |

基本流程是：建立客户端 → 定义 OpenQASM 和配置 → 准备报价 → 确认执行 → 读取结果。第 1–6 步组成一个完整实验。后续章节介绍文件输入、自动提交和批量实验，可根据自己的程序选择相应流程。确认执行后，平台开始安排实验并使用计算资源。

| 你的目的 | 从哪个入口开始 | 是否自动执行 |
| --- | --- | --- |
| 先看报价，再决定是否执行 | prepare_submission(...) → prepared.quote → prepared.confirm() | prepare 不执行；confirm 才批准原报价。 |
| 程序已决定执行一个新实验 | submit_job(...) | 默认自动批准并等待；返回完成任务，不包含结果下载。 |
| 继续等待已提交的实验 | get_submission / wait_for_submission / wait_for_job | 只查询原记录，不创建新任务。 |
| 一次准备多个具体实验 | prepare_submissions(...) | 返回整批报价；batch.confirm() 才执行。 |

## 1. 建立连接

先安装 SDK，并按“连接与认证”设置 QUANTUM_SOURCE_API_URL、QUANTUM_SOURCE_BACKEND 和 QUANTUM_SOURCE_ORGANIZATION_ID。本页使用该章节创建的 creds 保存认证凭据，并在创建客户端时显式传入 creds=creds。平台地址、设备和组织仍从环境变量读取。

- [连接与认证：设置平台地址和创建 creds](connection.html#temporary)

如果当前 Notebook 中已经创建了 creds，直接继续使用，跳过下面的凭据输入单元。独立运行本页或下载的 Notebook 时，先运行这个单元，输入平台 Client ID 和 Client Secret。

```python
from getpass import getpass
from quantum_source_sdk import ClientCredentials

creds = ClientCredentials(
    client_id=input("Platform Client ID: ").strip(),
    client_secret=getpass("Platform Client Secret: "),
)
```

将 creds 传给 SchedulerClient，创建本次实验使用的客户端。

```python
import os
from quantum_source_sdk import SchedulerClient, JobSubmissionConfig

scheduler = SchedulerClient(
    base_url=os.environ["QUANTUM_SOURCE_API_URL"],
    creds=creds,
    currency="OPEN",  # 使用 FULL 时在首次准备前改为 "FULL"
)
backend_id = os.environ["QUANTUM_SOURCE_BACKEND"]
organization_id = os.environ["QUANTUM_SOURCE_ORGANIZATION_ID"]
```

| 代码 | 作用 |
| --- | --- |
| SchedulerClient(base_url=..., creds=creds) | 使用平台地址和前面创建的 creds 建立客户端。 |
| currency="OPEN" / "FULL" | 在客户端构造时选择后续新准备任务的钱包；scheduler.currency 只读，默认 OPEN。 |
| backend_id | 本次选择的设备 ID 或 short_code。 |
| organization_id | 本次任务所属组织的 ID，用于任务归属、权限与共享名额；费用从当前用户所选钱包扣取。 |

如果尚未选设备，先用 scheduler.management.list_backend_classes() 浏览目录，再用 scheduler.get_backend_class(code) 获取详情。门集、输入格式、设备配置各自怎样读取，见 Core 设备查询专章。

- [Core 专章：设备查询、OpenQASM 格式与 configuration_data](capabilities-core.html)

使用 FULL 钱包时，在创建 scheduler 的连接单元中设置 currency="FULL"，再准备任务。通过 scheduler.currency 可以查看当前选择。已准备的任务沿用其报价中的币种。

## 2. 定义线路与任务

这个例子先对 q[0] 施加 H 门，再用 CX 将两个比特纠缠，最后测量到经典寄存器。SDK 和平台负责将程序转换成所选设备的执行格式。

```python
qasm = b'''OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0],q[1];
measure q -> c;
'''

config = JobSubmissionConfig(
    backend_class_id=backend_id,
    organization_id=organization_id,
    name="bell-core",
    shots=128,
    input_format="openqasm2",
    configuration_data={"circuit_representation": "logical"},
)
```

qasm 是线路内容的 bytes，以 b 开头的字符串可直接交给 file_content。已有普通字符串时使用 text.encode("utf-8")。JobSubmissionConfig 保存本次任务的运行设置。

| 配置字段 | 本例取值 | 含义 |
| --- | --- | --- |
| backend_class_id | backend_id | 目标设备的 ID 或 short_code。 |
| organization_id | organization_id | 明确指定任务归属的组织。 |
| name | bell-core | 用于识别实验的显示名称。 |
| shots | 128 | 这一个任务的采样次数。 |
| input_format | openqasm2 | 对应文件头中的 OpenQASM 版本。 |
| configuration_data | circuit_representation=logical | 声明这是逻辑线路；厂商专属选项也放在这个字典中。 |

## 3. 准备线路

把任务配置和线路交给 prepare_submission，取得一个可查看、可确认的准备对象。

```python
prepared = scheduler.prepare_submission(config, file_content=qasm)
submission_id = prepared.preparation.id
```

prepare_submission 上传线路并取得报价，返回 PreparedSubmission 对象。prepared 保存本次线路和报价；prepared.preparation.id 是本次提交的标识，后续确认和查询都使用它。

## 4. 查看报价

```python
prepared.quote
```

Notebook 会展示报价摘要：目标设备、任务数、总 shots、金额、币种和有效期。本例准备了一条 128-shots 线路，实际价格由平台返回。

当前平台只支持 public + stand 单通道。在创建客户端时用 currency 选择 OPEN 或 FULL，默认 OPEN。本示例选择 OPEN；使用 FULL 时，将连接单元中的 currency 改为 "FULL"，再准备任务。prepared.quote 展示本次实验的金额和币种，费用从当前用户选择的钱包扣除。两个钱包独立记账；切换钱包时，创建对应币种的客户端并重新获取报价。平台对已识别的模拟器任务报零平台积分，真机费用以返回的报价为准。

在报价有效期内确认即可执行本次实验。报价过期后，重新准备任务并查看新报价。

## 5. 确认执行

查看报价后，运行下面的代码批准这次实验。此步骤会提交任务并可能消耗真机资源。

```python
submission = prepared.confirm(wait=False)
```

wait=False 表示提交后立即返回，适合在下一步单独等待结果。返回值随发送进度为 QueuedSubmission 排队句柄、SubmissionReceipt 回执或 JobRead 任务记录；使用第 3 步保存的 submission_id 可以统一查询本次提交。

希望提交后直接等待完成时，使用 job = prepared.confirm()。它默认等待实验成功后返回 JobRead，再调用 scheduler.download_job_result(job) 读取结果。

## 6. 等待并读取结果

```python
job = scheduler.wait_for_submission(submission_id, timeout_seconds=300)
result = scheduler.download_job_result(job)
result.primary_value
```

| 调用或字段 | 输入 / 输出 | 用途 |
| --- | --- | --- |
| wait_for_submission(...) | 原 submission_id → 完成的 JobRead | 等待这条原提交进入可读取结果的阶段；timeout_seconds 包含本次排队及执行等待。 |
| download_job_result(job) | JobRead → UnifiedResult | 传入任务对象，下载并解析统一结果。 |
| result.result_kind | 结果类型 | 区分 counts 与 probabilities 等结果通道。 |
| result.primary_value | 主要结果值 | counts 为位串到整数次数的映射；probabilities 为概率结果。 |

## 可选：查询间隔与进度显示

取得任务对象后，可以用 scheduler.wait_for_job(job.id) 查询任务进度，下面沿用第 6 步的 job.id。如果你保存的是准备任务时得到的 submission_id，就使用 scheduler.wait_for_submission(submission_id) 等待结果。

```python
task = scheduler.wait_for_job(
    job.id,
    timeout=300,
    interval=2,
    verbose=True,
)
task.status
```

| 参数 | 省略时 | 作用 |
| --- | --- | --- |
| timeout | 沿用 scheduler.wait_timeout_seconds，默认 1800 秒 | 本次在客户端等待结果的时长，单位为秒。 |
| interval | 沿用 scheduler.poll_interval_seconds，默认 2 秒 | 两次状态查询之间的等待秒数，网络请求耗时另计。 |
| verbose | False | 任务状态变化时打印简短英文状态。 |
| timeout_seconds | timeout 的兼容名称 | 两个名称任选一个；同时填写时使用相同值。 |

这些参数控制本次客户端查询。wait_for_job 在任务完成、失败或取消后返回 JobRead，通过 task.status 查看最终状态。等待超时时，任务继续由平台处理；稍后再次调用查询方法即可。单次网络请求另有连接超时设置，因此实际等待时长还包含请求耗时。

## 从文件提交与输入语言

Core SDK 可以读取自己的 .qasm 文件，也可以接收 UTF-8 字节内容。文件输入使用 file_path，字节内容使用 file_content；按程序的版本设置 input_format。

| input_format | 文件内容 |
| --- | --- |
| openqasm2 | 以 OPENQASM 2.0 开头的线路文本 |
| openqasm3 | 以 OPENQASM 3.0 开头的线路文本 |

## 使用自己的 QASM 文件

将自己的 OpenQASM 文件放在 Notebook 所在目录，或提供文件的完整路径。下面以 OpenQASM 2 文件 my_circuit.qasm 为例，沿用前面创建的 scheduler、backend_id 和 organization_id。

```python
file_config = JobSubmissionConfig(
    backend_class_id=backend_id,
    organization_id=organization_id,
    name="my-qasm-experiment",
    shots=128,
    input_format="openqasm2",
)
file_prepared = scheduler.prepare_submission(
    file_config,
    file_path="my_circuit.qasm",
)
file_prepared.quote
```

把 my_circuit.qasm 换成自己的文件路径。使用 OpenQASM 3 文件时，将 input_format 设为 "openqasm3"。核对报价后调用 file_prepared.confirm() 执行，再将返回的任务对象传给 scheduler.download_job_result() 读取结果。

## JobSubmissionConfig 与设备选项

通用任务字段决定设备、采样次数和所属组织；configuration_data 设置设备的执行选项。通过所选设备的 configuration_schema 查看可选参数及其默认值。

下面以本源真机的逻辑线路为例，设置映射、线路优化和云端修正选项。在准备报价前创建这个 config，再将它交给 prepare_submission。

```python
config = JobSubmissionConfig(
    backend_class_id=backend_id,
    organization_id=organization_id,
    name="bell-core-configured",
    shots=128,
    input_format="openqasm2",
    configuration_data={
        "circuit_representation": "logical",
        "mapping": True,
        "optimization": False,
        "amend": True,
    },
)
```

本源 QCloudOptions 提供映射、优化和修正开关。在 QuantumFerry 中，将对应选项写入 configuration_data 即可使用：

| 选项 | 功能 | 本例设置 |
| --- | --- | --- |
| mapping | 启用本源云端拓扑映射，将逻辑比特到芯片物理比特的映射及门操作适配芯片连接关系。 | True：开启云端拓扑映射；已完成固定布局的物理线路使用 False。 |
| optimization | 控制本源云端的线路优化。 | False：关闭云端线路优化；需要开启时设置 True。 |
| amend | 控制本源提供的云端修正功能。 | True：启用修正；需要关闭时设置 False。 |

- [本源 QPanda3 官方说明：QCloudOptions](https://github.com/OriginQ/pyqpanda3-skill/blob/main/resources/api/qcloud/index.md)

## JobSubmissionConfig 参数参考

下表列出 JobSubmissionConfig 的公开字段。创建配置时填写设备、名称和 shots，再根据需要补充组织与设备选项。

| 字段 | 类型与默认值 | 用途和边界 |
| --- | --- | --- |
| backend_class_id | str，必填 | 设备 ID 或 short_code。 |
| name | str，必填 | 任务显示名称，便于在记录中识别实验。 |
| job_subcategory_id | str = "oth:oth" | 任务子类别。 |
| shots | int \| None = None | 实际必须提供正整数 shots，或在 configuration_data["shots"] 提供；两处同时提供必须相同。 |
| organization_id | str \| None = None | 所属组织的 ID；账号只有一个组织时可自动选择，属于多个组织时显式填写。 |
| configuration_data | Mapping \| None = None | 平台及设备配置，mapping 等选项作为字典中的键填写。 |
| execution_plan | str = "public" | 当前平台新任务为 public 单通道。 |
| queue_priority | str = "stand" | 当前平台使用 stand 队列。 |
| auto_approve_quote | bool = True | submit_job 的自动批准开关；False 时走显式审批或抛 QuoteApprovalRequired。 |
| verbose | bool = False | 准备成功后输出一条 job_prepared 事件，包含准备号、设备、shots、金额和币种。 |
| job_timeout_seconds | float = 86400.0 | 保存到 prepared 的默认执行等待预算；准备阶段等待上限取它与客户端 wait_timeout_seconds 的较小值。 |
| submitted_with | str = "sdk" | 记录客户端来源；普通调用保持默认。 |
| input_format | str \| None = None | None 在请求中转为 qasm；也可明确选择平台支持的 openqasm2/openqasm3。 |
| provider | str \| None = None | 仅设备标识尚无厂商前缀时补齐；已有 origin:... 等完整标识通常不填。 |

## 替代流程：程序自动准备、批准并等待

应用可以使用 submit_job 自动准备报价、批准并等待实验完成。下面沿用本页的 scheduler、qasm、backend_id 和 organization_id，作为 prepare/confirm 的另一种用法。

```python
automatic_config = JobSubmissionConfig(
    backend_class_id=backend_id,
    organization_id=organization_id,
    name="bell-core-automatic",
    shots=128,
    input_format="openqasm2",
    auto_approve_quote=True,
    job_timeout_seconds=300,
    verbose=True,
)

# 以下会创建并执行一个新任务，可能收费：
# finished = scheduler.submit_job(automatic_config, file_content=qasm)
# value = scheduler.download_job_result(finished).primary_value
```

submit_job 默认等待任务成功后返回 JobRead，再用 download_job_result 读取结果。需要先提交、稍后读取结果时，使用 prepare_submission 和 prepared.confirm(wait=False)。需要人工审阅报价时，使用本页第 3–5 步的 prepare / quote / confirm 流程。

| 进度开关 | 输出内容 | 使用时机 |
| --- | --- | --- |
| JobSubmissionConfig(verbose=True) | 准备完成的一条 JSON 事件 job_prepared | 准备任务并查看设备、shots 和报价摘要。 |
| scheduler.wait_for_job(..., verbose=True) | 本次等待中的英文任务状态变化 | 已有任务 ID，需要跟踪运行状态。 |

## 多任务与部分成功

多个独立实验可以一次准备成批次。configs 与 file_contents 按位置一一对应；下面准备两份 Bell 实验，每份使用 128 shots。

```python
configs = [
    JobSubmissionConfig(
        backend_class_id=backend_id,
        organization_id=organization_id,
        name=name,
        shots=128,
        input_format="openqasm2",
    )
    for name in ("bell-a", "bell-b")
]
batch = scheduler.prepare_submissions(
    configs,
    file_contents=[qasm, qasm],
)
```

```python
batch.quote
```

batch 是 PreparedBatch，batch.quote 汇总每条任务的报价。本例包含 2 条任务、总计 256 shots。核对整批报价后，调用 confirm 执行：

```python
submissions = batch.confirm(wait=False)
```

```python
jobs = batch.recover(wait=True, timeout=300)
values = [scheduler.download_job_result(item).primary_value for item in jobs]
values
```

batch.recover(wait=True, timeout=300) 等待已尝试提交的任务，结果按批次顺序返回。若批次只接受了一部分，通过 batch.job_ids 查看已建立的任务，通过 batch.unsubmitted_preparation_ids 查看尚待提交的准备记录；已接受的任务继续由平台执行。

## 超时或异常后查询原提交

等待超时或中断本地查询后，平台继续处理任务。再次运行第 6 步即可继续等待同一个实验；查看一次当前进度时，使用下面的查询。

```python
receipt = scheduler.get_submission(submission_id)
receipt.submission_status
```

queued 表示等待发送，submitting 表示正在发送。使用 submission_id 跟踪提交进度；取得任务 ID 后，也可用 scheduler.get_job(job_id) 查询任务。状态暂时无法确认时，SDK 会返回对应的平台异常，稍后查询同一记录即可。

| 异常 | 用户应如何处理 |
| --- | --- |
| APIError（HTTP 402） | 所选钱包余额不足。补足余额后继续确认；也可切换客户端钱包并重新准备报价。批次中已接受的任务继续执行。 |
| PlatformSubmissionError | 平台暂时无法确认发送结果；保留原提交 ID，查询原记录。 |
| ProviderSubmissionError | 包含厂商返回的公开错误说明。结合说明和原提交 ID 查询本次提交的状态。 |
| PlatformStatusError | 当前状态无法确认，包含未识别的厂商状态；稍后查询同一个任务。 |
| PlatformResultError | 结果暂时无法读取；查询原任务并再次读取原结果。 |
| JobLimitReachedError | 组织当前任务名额已满；核对已有任务及本批已接受的部分。 |
| JobNotCancelableError | 当前任务已不满足取消条件；继续查询原记录。 |

反馈问题时，提供错误说明和提交 ID 或任务 ID，便于定位对应记录。

## 取消与组织名额

需要取消实验时，调用 scheduler.cancel_job(submission_id)。平台尚未领取的排队任务可以取消，成功时返回 None 并释放预留。已进入发送阶段的任务返回 JobNotCancelableError，可以继续查询其运行结果。

当前每个组织最多同时保有 10 个尚未结束的任务，组织内成员、SDK Key 和设备共享这些名额。平台接受确认后占用名额，在确认完成、失败或取消后释放；排队、发送中和等待核实的任务持续占用。名额已满时，等待已有任务结束后再提交。每次实验同时按账号授权检查 shots 额度。

最终状态为 Failed 的任务按原币种退款；同一批次中的成功任务按各自报价结算。OPEN 退款沿用原有效期，有效期内的部分恢复为可用余额。具体费用与退款记录可在平台钱包和账单中查看。

## 完成后关闭连接

完成查询后调用 close() 释放本地 HTTP 连接，远端任务继续由平台管理。

```python
scheduler.close()
```

## 下一步

- [连接与认证](connection.html)
- [结果说明](results.html)
- [下载 Core Notebook](../../notebooks/sdk_core_current.ipynb)

## 下载本页 Jupyter Notebook

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

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