QuantumFerry 用户使用手册

PennyLane 操作、测量与梯度

本页介绍 QNode 的常用操作、测量返回值,以及广播、测量分组和梯度如何展开成具体实验。示例使用本 SDK 的 QuantumSourceDevice 和 PennyLane 0.45。

建立共同连接后,选择需要的实验示例学习。每个示例先构造线路并展示报价;执行时,取消对应 confirm 行的注释。confirm 和开启自动批准后的 QNode 调用会使用量子计算资源。

共同前置:创建一个逻辑线路设备

python
import os
import pennylane as qml
from pennylane import numpy as pnp
from quantum_source_sdk import SchedulerClient

scheduler = SchedulerClient(
    base_url=os.environ["QUANTUM_SOURCE_API_URL"],
    currency="OPEN",  # 如需 FULL,在准备任何任务前改为 FULL
)
backend_id = os.environ["QUANTUM_SOURCE_BACKEND"]
organization_id = os.environ["QUANTUM_SOURCE_ORGANIZATION_ID"]
dev = qml.device(
    "quantum_source.device",
    wires=2,
    backend=backend_id,
    organization_id=organization_id,
    scheduler=scheduler,
    auto_approve_quote=False,
)

平台认证由 QUANTUM_SOURCE_SDK_KEY 文件,或 QUANTUM_SOURCE_CLIENT_ID / QUANTUM_SOURCE_CLIENT_SECRET 提供。wires=2 声明两个逻辑 wire,插件和平台负责线路转换与映射。需要固定物理位置时,使用 Qiskit 布局流程。

查看可使用的 PennyLane 操作

python
plugin_caps = dev.capabilities
print("PennyLane 操作:", sorted(plugin_caps.operations))
print("可观察量:", sorted(plugin_caps.observables))
print("测量类型:", sorted(plugin_caps.measurement_processes))
当前插件操作常用写法解释
Identity、PauliX/Y/Z、Hadamard、S、T、SXqml.Hadamard(0)单 wire 的固定门。
RX、RY、RZ、PhaseShiftqml.RY(theta, 0)带弧度参数的单 wire 门。
CNOT、CY、CZ、CH、SWAPqml.CNOT(wires=[0, 1])双 wire 门;最终物理映射取决于所选设备的逻辑执行路径。
CRX、CRY、CRZ、ControlledPhaseShiftqml.CRY(theta, wires=[0, 1])参数化双 wire 门。
Toffoli、CSWAP在三个 wire 上使用将设备 wires 至少设为 3。
Barrier、GlobalPhase线路结构 / 全局相位操作按线路转换规则处理屏障和全局相位。

PennyLane 将可分解的高级操作转换为插件支持的操作,再生成 OpenQASM。当前工作流采用固定 wires 和末尾测量的静态线路;使用上表中的门及其可分解组合编程即可。查看目标硬件的原生门与连接图时,读取 dev.provider_capabilities。

可观察量示例处理方式
Pauli、Identity、Hadamardqml.expval(qml.Z(0))按测量基取得 counts 并还原统计量。
张量积 / 标量乘 / 求和qml.expval(0.5 * qml.Z(0) + qml.X(1))PennyLane 根据测量关系分组和后处理;总项数不一定等于任务数。
Hamiltonian / LinearCombinationqml.Hamiltonian([1.0, 0.5], [qml.X(0), qml.Z(0)])不对易项可能拆为多个测量基。
Hermitianqml.Hermitian(matrix, wires=0)有限且厄米的矩阵;必要时添加基变换。互相重叠且不能共同处理的矩阵可观察量需要分组。

测量与返回值

QNode 的 return有限 shots 返回怎样读取
qml.counts(wires=[0, 1])dict[str, int]键为测量位串,值为出现次数。
qml.probs(wires=[0, 1])形状 (4,) 的频率数组,顺序 00、01、10、11读取由 counts 归一化得到的各位串频率。
qml.expval(qml.Z(0))标量期望值读取可观察量的采样期望值估计。
qml.var(qml.Z(0))标量方差读取可观察量测量值的方差。
qml.sample(wires=[0, 1])通常为 (shots, 2) 的位数组;单 wire 会压缩 wire 轴按由 counts 重建的统计样本使用,样本顺序为重建顺序。
多个测量组成的 tuple按 return 顺序还原的 tuple逐项接收各测量结果,实际任务数见准备对象。

上表中的 counts 对应计算基 wires 测量;使用 counts(obs) 时,字典键为可观察量的本征值。数组结果按请求的接口返回 NumPy / PennyLane 包装,可根据形状和测量含义继续计算。有限 shots 工作流读取平台 counts;原生概率结果的用法见本页的概率扩展。

示例 A:同一个测量基返回三类统计量

下面构造带一个旋转门的线路,同时请求计数、概率和 Z 期望值。三种统计量共享同一计算基测量,通过 construct 和 prepare 查看本次报价。

python
@qml.set_shots(128)
@qml.qnode(dev, diff_method=None)
def experiment(theta):
    qml.RY(theta, wires=0)
    return (
        qml.counts(wires=[0], all_outcomes=True),
        qml.probs(wires=[0]),
        qml.expval(qml.Z(0)),
    )

tape = experiment.construct((0.42,), {})
prepared_statistics = dev.prepare(tape)
print(prepared_statistics.quote)

# 确认这个报价后,单独运行;这会执行本批次:
# counts, probabilities, expectation = prepared_statistics.confirm()

all_outcomes=True 使未出现的计算基结果也以 0 次显示。三个返回项依次对应 return 的三个请求。对于 N 次、一个 wire 的计数 C0/C1,插件计算 probs=[C0/N, C1/N]、Z 期望值=(C0−C1)/N。用 prepared_statistics.task_count 和 total_shots 查看实际任务量。

示例 B:为什么 128 shots 会变成 384 shots

shots 指每个展开后实验的采样次数。对同一 wire 同时测 X、Y、Z 时,插件会准备三个测量基不同的实验,每个 128 shots,总计 384 shots。

python
@qml.set_shots(128)
@qml.qnode(dev, diff_method=None)
def three_axes():
    qml.RY(0.37, wires=0)
    qml.RZ(0.28, wires=0)
    return qml.expval(qml.X(0)), qml.expval(qml.Y(0)), qml.expval(qml.Z(0))

prepared_axes = dev.prepare(three_axes.construct((), {}))
print("展开任务数:", prepared_axes.task_count)
print("总 shots:", prepared_axes.total_shots)
print(prepared_axes.quote)

# 决定执行这三个实验后再运行;返回 X、Y、Z 三个期望值:
# x_value, y_value, z_value = prepared_axes.confirm()

本例的准备结果为 3 条任务、384 总 shots。不同线路按测量关系分组:可对易项可以共享实验,矩阵测量可能增加基变换。准备对象中的任务数和总 shots 会展示本次展开结果。

示例 C:一个参数数组代表多个实验

下面把三个角度广播到同一 RY 门,每个角度执行 32 shots。结果沿广播维度排列,依次对应这三个角度。

python
angles = pnp.array([0.0, 0.5, 1.0], requires_grad=False)
broadcast_tape = qml.tape.QuantumScript(
    [qml.RY(angles, wires=0)],
    [qml.expval(qml.Z(0))],
    shots=32,
)
prepared_broadcast = dev.prepare(broadcast_tape)
print("任务 / 总 shots:", prepared_broadcast.task_count, prepared_broadcast.total_shots)
print(prepared_broadcast.quote)

# 决定执行后再运行;本例结果形状为 (3,):
# expectations = prepared_broadcast.confirm()

本例是 3 条任务、96 总 shots。广播和测量分组可以同时展开实验。需要为实验指定不同采样量时,分别创建带整数 shots 的 QuantumScript,再用 dev.prepare((tape_a, tape_b)) 一起准备。

示例 D:参数移位梯度与自动批准

PennyLane 的 parameter-shift 先生成参数移位线路,再由插件执行这些线路并返回统计量,最后组合成梯度。一次梯度计算可以包含前向实验和多条移位实验,每个具体批次都有对应报价。

python
# 独立的梯度分支:这台设备允许每个具体批次自动批准报价。
# qml.grad 调用会执行梯度所需的实验。
gradient_dev = qml.device(
    "quantum_source.device",
    wires=1,
    backend=backend_id,
    organization_id=organization_id,
    scheduler=scheduler,
    auto_approve_quote=True,
)

@qml.set_shots(32)
@qml.qnode(gradient_dev, interface="autograd", diff_method="parameter-shift")
def loss(theta):
    qml.RY(theta[0], wires=0)
    qml.RZ(theta[1], wires=0)
    return qml.expval(qml.X(0))

theta = pnp.array([0.2, 0.3], requires_grad=True)

# 执行本次梯度实验时,取消以下代码的注释:
# gradient = qml.grad(loss)(theta)
# print(gradient)
# print(gradient_dev.job_ids)
# print(gradient_dev.total_credits_used)

结果是形状 (2,) 的梯度,分别对应 theta[0] 和 theta[1]。有限 shots 会带来采样噪声。项目离线测试中的同结构例子包含一次前向和四次移位,共 5 条任务、160 shots;实际调用的工作量可查看执行批次与报价。

自动求导流程使用 parameter-shift,并通过 auto_approve_quote=True 批准各个实验批次。需要逐批查看报价时,显式管理具体 tapes 和准备对象。auto_approve_quote=False 的直接执行入口以 QuoteApprovalRequired 返回当前准备信息;恢复已有实验时,继续操作该准备对象。

可选扩展:shots=None 的原生概率

目标设备提供原生 probabilities 时,可用 shots=None 请求单个 qml.probs,并通过 provider_probability_shots 指定平台执行与计费预算。该模式读取设备的概率结果;日常采样实验使用前面的有限整数 shots 流程。

python
# 为提供原生 probabilities 的设备构造概率测量分支。
probability_dev = qml.device(
    "quantum_source.device",
    wires=2,
    backend=backend_id,
    organization_id=organization_id,
    scheduler=scheduler,
    provider_probability_shots=128,
    auto_approve_quote=False,
)
probability_tape = qml.tape.QuantumScript(
    [qml.Hadamard(0), qml.CNOT(wires=[0, 1])],
    [qml.probs(wires=[0, 1])],
    shots=None,
)

# 先确认这台设备的 probabilities 契约,再准备:
# probability_quote = probability_dev.prepare(probability_tape)

返回的归一化概率数组按请求的 wire 顺序排列。插件会检查概率通道及其数据;counts、sample、expval、var 则使用有限整数 shots。

查看本次实验的任务量和费用

公开属性范围使用方式
prepared.task_count / total_shots这次准备后的具体批次确认前查看本批次任务数和总采样次数。
prepared.job_ids该批次已取得的任务号发送队列中的提交可先用 attempted_preparation_ids 查询。
dev.last_execution_metadata最近一次执行 / 准备批次读取最近一批次的任务数和 shots。
dev.job_ids当前设备实例记录的任务号保存任务号,供当前会话或稍后通过 Core 查询。
dev.total_credits_used当前设备实例已读取的完成任务费用汇总该实例读取到的已完成结果显式费用。

等待超时后,再次调用原 prepared.result(timeout=...) 即可继续读取同一实验。中断时,插件会尝试取消本批仍待发送的任务;已发送任务继续查询结果。恢复与取消的具体用法见 PennyLane 主教程和任务查询章节。

关闭本地连接

python
# 完成实验后关闭本次创建的设备与连接。
dev.close()
# gradient_dev.close()
# probability_dev.close()
scheduler.close()

API 参考与相关章节

设备构造、线路准备、执行、结果读取与恢复的参数和返回值,统一在 PennyLane API 参考中查阅。

下载本页 Jupyter Notebook

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

下载《测量、shots 与梯度》Notebook(.ipynb)

输入关键词;所有搜索都在本机完成。