Skip to content

量子态制备 ​

学习如何使用 pyqpanda3 中的 Encode 类将经典数据编码为量子态。本教程涵盖 14 种编码方法——从简单的基础编码到高级的稀疏和近似 MPS 技术——包括数学基础、代码示例以及选择合适方法的实用指南。


目录 ​


为什么量子态制备很重要 ​

量子态制备——也称为数据编码或数据嵌入——是将经典数据映射到量子态的过程。它几乎是所有量子算法的基本步骤:

  • 量子机器学习——核方法、变分类器和量子神经网络都需要将经典数据加载到量子寄存器中。
  • 量子模拟——需要先制备被研究物理系统的初始态。
  • 量子算法——Grover 搜索、量子相位估计和哈密顿量模拟都假设特定的输入态。

编码方法的选择直接影响线路深度、量子比特数、表达能力,并最终影响算法性能。pyqpanda3 通过 Encode 类提供了 14 种编码方法,每种方法针对不同的数据特性进行了优化。


用 init_state 直接设置初始态 ​

在 0.4.1 中,CPUQVM 新增了 init_state:如果你已经知道目标态的复振幅,可以直接把它设置为模拟器的初始态,完全绕开编码线路。模拟器会在每次 run() 前重置为这个初态;无参调用 init_state() 恢复默认的 |0...0>。

python
import numpy as np
from pyqpanda3.core import CPUQVM, QProg, X, measure

qvm = CPUQVM()

# 直接设置初始态为贝尔态 (|00>+|11>)/sqrt(2),无需编码线路
qvm.init_state(np.array([1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)], dtype=complex))

prog = QProg() << measure([0, 1], [0, 1])
qvm.run(prog, 1000)
print("bell:", qvm.result().get_counts())

# init_state 在每次 run 前重新生效:X(1) 把贝尔态中的 |11> 翻转为 |10>
qvm.run(QProg() << X(1) << measure([0, 1], [0, 1]), 1000)
print("after X(1):", qvm.result().get_counts())

qvm.init_state()  # 恢复默认 |0...0>
qvm.run(prog, 1000)
print("default:", qvm.result().get_counts())

运行结果(计数随随机种子波动):

bell: {'00': 492, '11': 508}
after X(1): {'01': 499, '10': 501}
default: {'00': 1000}

说明:MPSQVM 的 C++ init_state 未在 Python 绑定中暴露(0.4.1),Python 侧请使用 CPUQVM.init_state。


Encode 类 ​

pyqpanda3.core 中的 Encode 类是所有量子态制备方法的统一接口。你创建一个实例,调用编码方法,然后提取结果。

python
from pyqpanda3 import core

enc = core.Encode()

核心方法 ​

get_circuit()——返回实现编码的 QCircuit:

python
enc = core.Encode()
enc.amplitude_encode([0, 1], [0.5, 0.5, 0.5, 0.5])
circuit = enc.get_circuit()
prog = core.QProg()
prog << circuit

get_out_qubits()——返回输出量子比特索引。某些方法内部使用辅助量子比特,因此输出量子比特可能与输入量子比特不同:

python
enc = core.Encode()
norm = sum(x**2 for x in [0.5, 0.3, 0.2, 0.4, 0.1, 0.6, 0.3, 0.5]) ** 0.5
enc.dc_amplitude_encode(list(range(7)), [x/norm for x in [0.5, 0.3, 0.2, 0.4, 0.1, 0.6, 0.3, 0.5]])
out_qubits = enc.get_out_qubits()

get_fidelity(data)——计算编码态与目标态之间的保真度。接受 List[float] 或 List[complex]。保真度范围从 0(正交)到 1(相同):

F=‖⟨ψencoded∥ψtarget⟩‖2
python
enc = core.Encode()
enc.amplitude_encode([0, 1], [0.5, 0.5, 0.5, 0.5])
fidelity = enc.get_fidelity([0.5, 0.5, 0.5, 0.5])  # 1.0 for exact methods

工作流程 ​

mermaid
graph LR
    A["core.Encode()"] --> B["enc.method(qubits, data)"]
    B --> C["enc.get_circuit()"]
    B --> D["enc.get_out_qubits()"]
    B --> E["enc.get_fidelity(data)"]
    C --> F["QProg() << circuit"]
    F --> G["CPUQVM().run(prog, shots)"]

    style A fill:#4a90d9,color:#fff
    style B fill:#50b86c,color:#fff
    style G fill:#c0392b,color:#fff

方法选择指南 ​

mermaid
graph TD
    START["经典数据"] --> Q1{"数据类型?"}
    Q1 -->|"二进制字符串"| BASIC["basic_encode"]
    Q1 -->|"实数/复数向量"| Q2{"数据是否稀疏?"}

    Q2 -->|"是"| Q3{"稀疏程度?"}
    Q3 -->|"少量非零元素"| SPARSE["sparse_isometry"]
    Q3 -->|"中等稀疏"| EFF["efficient_sparse"]
    Q3 -->|"已知基态"| DS["ds_quantum_state_preparation"]

    Q2 -->|"否(稠密)"| Q4{"精度要求?"}
    Q4 -->|"精确"| Q5{"数据规模?"}
    Q4 -->|"近似即可"| MPS["approx_mps_encode"]

    Q5 -->|"小规模"| AMP["amplitude_encode"]
    Q5 -->|"中等规模"| Q6{"深度限制?"}
    Q6 -->|"最小化深度"| RECUR["amplitude_encode_recursive"]
    Q6 -->|"均衡"| SCHMIDT["schmidt_encode"]

    START -->|"特征向量"| Q7{"密度?"}
    Q7 -->|"每量子比特1个角度"| ANGLE["angle_encode"]
    Q7 -->|"每量子比特2个角度"| DENSE["dense_angle_encode"]
    Q7 -->|"需要纠缠"| IQP["iqp_encode"]

    START -->|"分块数据"| BID["bid_amplitude_encode"]

    style BASIC fill:#3498db,color:#fff
    style AMP fill:#2ecc71,color:#fff
    style ANGLE fill:#e67e22,color:#fff
    style MPS fill:#d35400,color:#fff
    style SPARSE fill:#1abc9c,color:#fff

1. 基础编码 ​

方法: basic_encode(qubits, data: str)

将二进制字符串直接映射到计算基态。字符串中的每个字符对应一个量子比特:'1' 施加 X 门(比特翻转),'0' 使量子比特保持基态 |0⟩。这是最简单的编码方式——最多使用 n 个单量子比特门,不产生纠缠。

给定长度为 n 的二进制字符串 b=bn−1…b0,编码制备:

∥b⟩=∥bn−1⟩⊗⋯⊗∥b0⟩,gatei={Xbi=1Ibi=0
属性值
量子比特数n(字符串长度)
深度1
精确是
python
from pyqpanda3 import core

enc = core.Encode()
enc.basic_encode([0, 1, 2], "101")

prog = core.QProg()
prog << enc.get_circuit() << core.measure([0, 1, 2], [0, 1, 2])
machine = core.CPUQVM()
machine.run(prog, 1000)
print(machine.result().get_counts())  # {'101': 1000}

运行结果:

{'101': 1000}

适用场景:编码经典比特串、制备计算基态、测试。


2. 角度编码 ​

方法: angle_encode(qubits, data, gate_type=GateType.RY)

将每个数据值映射到对应量子比特上的旋转角度。这是量子机器学习中最常用的编码方式之一,因为它产生浅层线路(深度 1),并通过旋转轴的选择提供可调的表达能力。

对于数据向量 x=(x0,…,xn−1):

∥ψ⟩=⨂i=0n−1Rg(xi)∥0⟩

使用默认的 RY 门:RY(θ)|0⟩=cos⁡(θ/2)|0⟩+sin⁡(θ/2)|1⟩。其他支持的类型包括 GateType.RX 和 GateType.RZ。

属性值
量子比特数n(数据长度)
深度1
精确否(信息压缩)
python
from pyqpanda3 import core

# 默认使用 RY 门
enc = core.Encode()
enc.angle_encode([0, 1, 2], [0.5, 1.0, 1.5])

# 使用 RX 门
enc2 = core.Encode()
enc2.angle_encode([0, 1, 2], [0.5, 1.0, 1.5], core.GateType.RX)

适用场景:QML 特征编码、变分线路初始层、浅层线路。


3. 密集角度编码 ​

方法: dense_angle_encode(qubits, data)

使用 RY 和 RZ 两种旋转将两个数据值打包到每个量子比特中,与标准角度编码相比,将每个量子比特存储的信息量翻倍。这在量子比特数量有限时非常有吸引力。

对于偶数长度 2n 的数据向量,编码使用 n 个量子比特,每个量子比特施加两次旋转:

∥ψ⟩=⨂i=0n−1RZ(x2i+1)⋅RY(x2i)∥0⟩

量子比特 i 的最终单量子比特态为:

RZ(β)⋅RY(α)∥0⟩=cos(α2)e−iβ/2∥0⟩+sin(α2)eiβ/2∥1⟩
属性值
量子比特数m/2(数据长度的一半)
深度2
精确否
python
enc = core.Encode()
# 6 个值编码到 3 个量子比特
enc.dense_angle_encode([0, 1, 2], [3.14, 3.14, 0.5, 1.0, 0.3, 0.7])

适用场景:量子比特数量有限、高维特征向量。


4. 振幅编码 ​

方法: amplitude_encode(qubits, data)

将归一化的经典向量直接映射到多量子比特量子态的振幅上。这是信息密度最高的精确编码方式:n 个量子比特可以表示 2n 个振幅。线路使用受控旋转和均匀受控门来精确设置每个振幅。

给定归一化的 x=(x0,…,x2n−1),其中 |x|=1:

∥ψ⟩=∑i=02n−1xi∥i⟩

接受 List[float](实数)或 List[complex](复数振幅)。数据会自动归一化。对于实数输入,态矢量与输入完全匹配。对于复数输入 x∈C2n,每个元素的幅值和相位都会被编码。

属性值
量子比特数log2⁡(N)
门数实数 O(N),复数 O(Nlog⁡N)
精确是
python
import numpy as np
from pyqpanda3 import core

# 实数值:等叠加态
enc = core.Encode()
enc.amplitude_encode([0, 1], [0.5, 0.5, 0.5, 0.5])
print(f"Fidelity: {enc.get_fidelity([0.5, 0.5, 0.5, 0.5])}")

# 复数值
enc2 = core.Encode()
data = [0.5+0j, 0.5j, -0.5, 0.5-0.5j]
norm = np.linalg.norm(data)
enc2.amplitude_encode([0, 1], [complex(x/norm) for x in data])

# 运行
prog = core.QProg()
prog << enc.get_circuit() << core.measure([0, 1], [0, 1])
machine = core.CPUQVM()
machine.run(prog, 1000)
print(machine.result().get_counts())

运行结果:

Fidelity: 1.0
{'00': 260, '01': 264, '10': 240, '11': 236}

适用场景:精确数据表示、中等规模数据(最多约 16 个量子比特)、为量子算法加载经典数据。


5. 递归振幅编码 ​

方法: amplitude_encode_recursive(qubits, data)

与标准振幅编码结果相同,但使用自顶向下的递归分解策略。线路结构与标准方法不同,这在具有特定连接模式的硬件上可能具有更好的并行性。接受 List[float] 和 List[complex]。

给定归一化的 x,定义:

α=∑i=02n−1−1∥xi∥2,β=∑i=2n−12n−1∥xi∥2

第一次旋转设置 RY(θ)|0⟩=α|0⟩+β|1⟩,然后对每一半递归处理:

∥ψ⟩=α∥0⟩⊗∥ψupper⟩+β∥1⟩⊗∥ψlower⟩

接受 List[float] 和 List[complex]。

属性值
量子比特数log2⁡(N)
深度O(N),递归结构
精确是
python
import numpy as np

enc = core.Encode()
data = [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25]
norm = np.linalg.norm(data)
enc.amplitude_encode_recursive([0, 1, 2], [x/norm for x in data])
print(f"Fidelity: {enc.get_fidelity([x/norm for x in data])}")

运行结果:

Fidelity: 1.0000000000000004

适用场景:与振幅编码相同,但线路结构更适合硬件执行。


6. IQP 编码 ​

方法: iqp_encode(qubits, data, control_list=[], bool_inverse=False, repeats=1)

施加由 Hadamard 门、对角旋转和纠缠相位门组成的结构化模式。IQP 线路被认为在经典上难以模拟。

线路为 U=H⊗n⋅UD⋅H⊗n,其中:

UD=∏(i,j)∈ERZZij(ϕij)⋅∏k=0n−1RZk(xk)

E 是来自 control_list 的边集。参数:control_list(RZZ 门的量子比特对)、bool_inverse(是否施加逆操作)、repeats(线路重复次数)。

属性值
量子比特数n(数据长度)
深度每次重复 O(1)(可交换门)
精确否(概率性)
python
# 基本 IQP
enc = core.Encode()
enc.iqp_encode([0, 1, 2], [0.5, 1.0, 1.5])

# 带纠缠和重复
enc2 = core.Encode()
enc2.iqp_encode([0, 1, 2], [0.5, 1.0, 1.5],
                control_list=[(0,1), (1,2)], repeats=2)

prog = core.QProg()
prog << enc.get_circuit() << core.measure([0,1,2], [0,1,2])
machine = core.CPUQVM()
machine.run(prog, 1000)
print(machine.result().get_counts())

运行结果:

{'101': 100, '000': 139, '100': 132, '001': 122, '010': 131, '011': 134, '110': 126, '111': 116}

适用场景:QML 核方法、基于纠缠的特征映射、经典难模拟线路。


7. Schmidt 编码 ​

方法: schmidt_encode(qubits, data, cutoff=0)

使用 Schmidt 分解将目标态递归地分解为双分部组件。cutoff 参数截断较小的奇异值,以更少的门实现近似编码。

对于 n 量子比特态分解为子系统 A 和 B:

∥ψ⟩=∑k=1rσk∥uk⟩A⊗∥vk⟩B

σk<cutoff 的项被丢弃,以深度换取精度。

属性值
量子比特数log2⁡(N)
深度取决于 Schmidt 秩
精确cutoff=0 时精确
python
enc = core.Encode()
data = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]
norm = np.linalg.norm(data)
data = [x/norm for x in data]

# 精确编码
enc.schmidt_encode([0, 1, 2], data, cutoff=0)
print(f"Fidelity (exact): {enc.get_fidelity(data):.6f}")

# 近似编码
enc2 = core.Encode()
enc2.schmidt_encode([0, 1, 2], data, cutoff=1e-6)
print(f"Fidelity (cutoff): {enc2.get_fidelity(data):.6f}")

运行结果:

Fidelity (exact): 1.000000
Fidelity (cutoff): 1.000000

适用场景:低 Schmidt 秩态、以精度换取深度、层次化数据。


8. 分治振幅编码 ​

方法: dc_amplitude_encode(qubits, data)

将数据向量分成两半,递归编码每一半。使用辅助量子比特进行分解,产生比标准振幅编码更浅的线路,但需要更多量子比特。始终使用 get_out_qubits() 来确定哪些量子比特承载最终的编码态。

算法计算每次分割的范数 α=|x(0)| 和 β=|x(1)|,在控制量子比特上施加旋转编码 (α,β),然后对两半递归处理。对于长度为 2n 的数据,所需总量子比特数为 2n−1。

属性值
量子比特数数据长度 2n 需要 2n−1
深度O(log⁡N)
精确是
python
import numpy as np

enc = core.Encode()
data = [0.5, 0.3, 0.2, 0.4, 0.1, 0.6, 0.3, 0.5]
norm = np.linalg.norm(data)
enc.dc_amplitude_encode(list(range(7)), [x/norm for x in data])

out_qubits = enc.get_out_qubits()
prog = core.QProg()
prog << enc.get_circuit()
prog << core.measure(out_qubits, list(range(len(out_qubits))))
machine = core.CPUQVM()
machine.run(prog, 1000)
print(machine.result().get_counts())

运行结果:

{'101': 297, '000': 191, '100': 14, '001': 77, '010': 35, '011': 131, '110': 69, '111': 186}

适用场景:线路深度比量子比特数更重要时、NISQ 硬件。


9. BID 振幅编码 ​

方法: bid_amplitude_encode(qubits, data, split=-1)

块逆分解:将数据向量划分为 2s 个块,独立编码每个块,然后通过多路旋转进行组合。split 参数控制块大小;-1 为自动选择。与 dc_amplitude_encode 类似,输出量子比特可能与输入量子比特不同,因此始终使用 get_out_qubits() 检查。

对于归一化向量 x∈C2n 划分为 2s 个块:

∥ψ⟩=∑k=02s−1∥xk∥⋅∥k⟩⊗∥x~k⟩

其中 |x~k⟩=xk/|xk| 是归一化的块态。

属性值
量子比特数log2⁡(N)
深度每块 O(N/2s)
精确是
python
import numpy as np

enc = core.Encode()
data = [0.1, 0.3, 0.2, 0.4, 0.15, 0.25, 0.35, 0.45]
norm = np.linalg.norm(data)
enc.bid_amplitude_encode([0, 1, 2], [x/norm for x in data], split=3)

适用场景:具有自然分块结构的结构化数据、可调的编码复杂度。


10. 双稀疏态制备 ​

方法: ds_quantum_state_preparation(qubits, data)

处理大多数元素为零的稀疏数据。复杂度取决于非零元素的数量 s,而不是 2n。

给定稀疏态 {(bk,αk)}:|ψ⟩=∑kαk|bk⟩

接受四种数据类型:

类型描述
Dict[str, float]稀疏实数值态
Dict[str, complex]稀疏复数值态
List[float]稠密向量(自动检测稀疏性)
List[complex]稠密向量(自动检测稀疏性)
属性值
量子比特数n + 可能的辅助比特
深度O(s⋅n)
精确是
python
# 稀疏字典格式
enc = core.Encode()
data_dict = {"000": 0.40, "001": 0.91, "111": 0.08}
norm = sum(v**2 for v in data_dict.values()) ** 0.5
enc.ds_quantum_state_preparation([0,1,2,3,4,5],
    {k: v/norm for k, v in data_dict.items()})
out_qubits = enc.get_out_qubits()

# 稠密向量格式(自动检测稀疏性)
enc2 = core.Encode()
norm = sum(x**2 for x in [0.0, 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5]) ** 0.5
enc2.ds_quantum_state_preparation(list(range(6)),
    [x/norm for x in [0.0, 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5]])

适用场景:含有大量零的数据、以 ket 符号表示的态。


11. 稀疏等距编码 ​

方法: sparse_isometry(qubits, data)

使用 Givens 旋转和受控操作构建等距映射,将 |0⟩⊗n 映射到目标稀疏态。与 ds_quantum_state_preparation 不同,此方法使用直接的等距方法而非双稀疏分解。

V∥0⟩⊗n=∑kαk∥bk⟩

等距被分解为一系列 Givens 旋转和受控操作。复杂度随非零振幅数量 s 缩放,而非希尔伯特空间维度 2n。

接受与 ds_quantum_state_preparation 相同的四种数据类型。

属性值
量子比特数n
深度O(s⋅n)
精确是
python
enc = core.Encode()
state = {
    "000": complex(0.37, 0.44),
    "001": complex(0.20, 0.34),
    "010": complex(0.53, 0.25),
    "100": complex(0.20, 0.35)
}
norm = sum(abs(v)**2 for v in state.values()) ** 0.5
state = {k: v/norm for k, v in state.items()}
enc.sparse_isometry([0, 1, 2], state)

prog = core.QProg()
prog << enc.get_circuit() << core.measure([0,1,2], [0,1,2])
machine = core.CPUQVM()
machine.run(prog, 1000)
print(machine.result().get_counts())

运行结果:

{'000': 325, '100': 183, '001': 154, '010': 338}

适用场景:稀疏态的精确编码、具有已知基态标签的态。


12. 高效稀疏编码 ​

方法: efficient_sparse(qubits, data)

通过寻找唯一标识所有非零基态的最小区分比特集 D,然后仅编码缩减的 2|D| 维空间来优化稀疏制备。对于聚集的稀疏模式,比 sparse_isometry 实现更好的门计数。

线路首先在区分量子比特上编码振幅分布,然后使用条件操作设置剩余量子比特。这避免了编码完整的 2n 维空间。

接受与 sparse_isometry 相同的数据类型。

属性值
量子比特数n
深度最佳情况 O(s+n)
精确是
python
enc = core.Encode()
data_dict = {"000": 0.40, "001": 0.91, "111": 0.08}
norm = sum(v**2 for v in data_dict.values()) ** 0.5
enc.efficient_sparse([0, 1, 2], {k: v/norm for k, v in data_dict.items()})
# 或使用稠密向量
enc2 = core.Encode()
data = [0.5, 0.3, 0.2, 0.4, 0.1, 0.6, 0.3, 0.5]
norm = sum(x**2 for x in data) ** 0.5
enc2.efficient_sparse([0,1,2], [x/norm for x in data])

适用场景:门计数关键的稀疏态、具有共享比特前缀的模式。


13. 近似 MPS 编码 ​

方法: approx_mps_encode(qubits, data, layers=3, sweeps=100, double2float=False)

将目标态表示为矩阵乘积态(MPS),并迭代优化线路以近似它。以牺牲精确精度为代价大幅减少线路深度。

∥ψ⟩=∑i1,…,inTr(A1i1A2i2⋯Anin)∥i1…in⟩

每个 MPS 张量被映射到单量子比特和双量子比特门,在 sweeps 次迭代中进行优化。更多的 layers 产生更高的保真度但更深的线路。接受 List[float] 和 List[complex]。

参数默认值描述
layers3MPS 层数(表达能力)
sweeps100优化迭代次数
double2floatFalse转换为 float32 以加速
属性值
量子比特数log2⁡(N)
深度O(n⋅layers)
精确否(近似)
python
import numpy as np
from pyqpanda3 import core

np.random.seed(42)
data = np.random.randn(8)
data = (data / np.linalg.norm(data)).tolist()

# 默认参数
enc = core.Encode()
enc.approx_mps_encode([0,1,2], data, layers=3, sweeps=100)
print(f"Fidelity (3 layers): {enc.get_fidelity(data):.6f}")

# 更高保真度
enc2 = core.Encode()
enc2.approx_mps_encode([0,1,2], data, layers=5, sweeps=200)
print(f"Fidelity (5 layers): {enc2.get_fidelity(data):.6f}")

运行结果:

Fidelity (3 layers): 1.000000
Fidelity (5 layers): 1.000000

适用场景:大规模数据向量、低纠缠态、NISQ 设备、近似编码可接受时。


14. 拓扑感知 MPS 编码 ​

topology_aware_mps_encode() 可以看作近似 MPS 编码的硬件感知版本。它仍然构造近似的振幅编码态,但会把所有双量子比特操作限制在你提供的连通边上,因此当你已经知道目标设备的连接关系、并希望在态制备阶段就避免额外 SWAP 时,这个接口非常合适。

python
from pyqpanda3.core import Encode

data = [0.25] * 16
topology = [[0, 1], [1, 2], [2, 3]]

encoder = Encode()
encoder.topology_aware_mps_encode(
    [0, 1, 2, 3],
    data,
    topology,
    depth=3,
)

print(type(encoder.get_circuit()).__name__)
print(round(encoder.get_fidelity(data), 6))

预期输出:

text
QCircuit
1.0

当你更看重保真度时,可以适当增大 depth。同时要确保输入振幅已经归一化,而且编码所使用的拓扑图在这些量子比特上保持连通。


编码方法比较 ​

方法量子比特数深度精确数据类型最适合
basic_encoden1是str基态
angle_encoden1否float[]QML 特征
dense_angle_encoden/22否float[]紧凑特征
amplitude_encodelog2⁡NO(N)是float[], complex[]精确加载
amplitude_encode_recursivelog2⁡NO(N)是float[], complex[]结构化态
iqp_encodenO(1)否float[]QML 核方法
schmidt_encodelog2⁡N可变精确/近似float[]低秩态
dc_amplitude_encode2n−1O(log⁡N)是float[]浅层线路
bid_amplitude_encodelog2⁡N可变是float[]分块数据
ds_quantum_state_preparationn+辅助O(sn)是Dict, float[], complex[]稀疏态
sparse_isometrynO(sn)是Dict, float[], complex[]稀疏精确
efficient_sparsenO(s+n)是Dict, float[], complex[]高效稀疏
approx_mps_encodelog2⁡NO(nL)否float[]大规模态
topology_aware_mps_encodelog2⁡NO(nL)否float[], complex[]硬件感知

决策流程图 ​

以下流程图根据你的数据特性和约束条件,引导你选择最合适的编码方法:

mermaid
flowchart TD
    A[开始:选择编码方法] --> B{数据类型?}
    B -->|二进制字符串| C[basic_encode]
    B -->|实数/复数向量| D{稀疏性?}
    B -->|QML 特征向量| E{量子比特预算?}

    D -->|稠密,全非零| F{需要精确?}
    D -->|稀疏,少量非零| G{s < n?}
    D -->|低秩结构| H{秩已知?}

    F -->|是| I{线路深度预算?}
    F -->|否,近似即可| J[approx_mps_encode]

    I -->|充裕| K[amplitude_encode]
    I -->|紧张| L[dc_amplitude_encode]

    G -->|是| M[efficient_sparse]
    G -->|否| N[sparse_isometry]

    H -->|是| O[schmidt_encode]
    H -->|否| P[bid_amplitude_encode]

    E -->|量子比特充足| Q[angle_encode]
    E -->|量子比特有限| R[dense_angle_encode]
    E -->|需要纠缠| S[iqp_encode]

    style C fill:#c8e6c9
    style K fill:#bbdefb
    style J fill:#fff9c4
    style M fill:#ffe0b2
    style Q fill:#e1bee7

量子比特和门代价汇总 ​

对于长度为 N=2n 的数据向量:

方法所需量子比特门数线路深度
basic_encodenO(n)O(1)
angle_encodenO(n)O(1)
dense_angle_encode⌈n/2⌉O(n)O(1)
amplitude_encodenO(2n)O(2n)
amplitude_encode_recursivenO(2n)O(n)
iqp_encodenO(n2)O(1)
schmidt_encodenO(2nr)O(2nr)
dc_amplitude_encodenO(n2n)O(n)
bid_amplitude_encodenO(n2n/2)O(n2n/2)
ds_quantum_state_preparationn+O(1)O(sn)O(sn)
sparse_isometrynO(sn)O(sn)
efficient_sparsenO(s+n)O(s+n)
approx_mps_encodenO(nL)O(nL)
topology_aware_mps_encodenO(nL)O(nL)

其中 s 是非零振幅的数量,r 是 Schmidt 秩,L 是 MPS 层数。


完整示例 ​

本示例使用多种方法编码真实分类数据,并比较线路统计信息和保真度:

python
from pyqpanda3 import core
import numpy as np

# 样本特征向量(归一化)
samples = [
    [0.50, 0.35, 0.60, 0.50],
    [0.80, 0.20, 0.30, 0.45],
    [0.15, 0.70, 0.55, 0.40],
]

for i, raw in enumerate(samples):
    norm = np.linalg.norm(raw)
    data = [x / norm for x in raw]
    print(f"\n--- Sample {i+1} ---")

    # Angle encoding (4 qubits)
    enc1 = core.Encode()
    enc1.angle_encode([0,1,2,3], raw)
    c1 = enc1.get_circuit()

    # Amplitude encoding (2 qubits)
    enc2 = core.Encode()
    enc2.amplitude_encode([0,1], data)
    c2 = enc2.get_circuit()

    # IQP encoding (4 qubits, with entanglement)
    enc3 = core.Encode()
    enc3.iqp_encode([0,1,2,3], raw, control_list=[(0,1),(1,2),(2,3)])
    c3 = enc3.get_circuit()

    # MPS approximation (2 qubits)
    enc4 = core.Encode()
    enc4.approx_mps_encode([0,1], data, layers=3, sweeps=100)
    c4 = enc4.get_circuit()

    # Schmidt encoding (2 qubits)
    enc5 = core.Encode()
    enc5.schmidt_encode([0,1], data, cutoff=0)
    c5 = enc5.get_circuit()

    print(f"  Angle:     {c1.size()} gates, depth {c1.depth()}")
    print(f"  Amplitude: {c2.size()} gates, depth {c2.depth()}, "
          f"fidelity {enc2.get_fidelity(data):.4f}")
    print(f"  IQP:       {c3.size()} gates, depth {c3.depth()}")
    print(f"  MPS:       {c4.size()} gates, depth {c4.depth()}, "
          f"fidelity {enc4.get_fidelity(data):.4f}")
    print(f"  Schmidt:   {c5.size()} gates, depth {c5.depth()}, "
          f"fidelity {enc5.get_fidelity(data):.4f}")

    # Run amplitude-encoded circuit
    prog = core.QProg()
    prog << c2 << core.measure([0,1], [0,1])
    machine = core.CPUQVM()
    machine.run(prog, 1000)
    print(f"  Counts: {machine.result().get_counts()}")

运行结果:

--- 样本 1 ---
  角度:     4 个门, 深度 1
  振幅:     5 个门, 深度 5, 保真度 1.0000
  IQP:      1 个门, 深度 11
  MPS:      3 个门, 深度 19, 保真度 1.0000
  Schmidt:  5 个门, 深度 3, 保真度 1.0000
  计数: {'00': 262, '01': 110, '10': 363, '11': 265}

--- 样本 2 ---
  角度:     4 个门, 深度 1
  振幅:     5 个门, 深度 5, 保真度 1.0000
  IQP:      1 个门, 深度 11
  MPS:      3 个门, 深度 21, 保真度 1.0000
  Schmidt:  5 个门, 深度 3, 保真度 1.0000
  计数: {'00': 635, '01': 40, '10': 100, '11': 225}

--- 样本 3 ---
  角度:     4 个门, 深度 1
  振幅:     5 个门, 深度 5, 保真度 1.0000
  IQP:      1 个门, 深度 11
  MPS:      3 个门, 深度 19, 保真度 1.0000
  Schmidt:  5 个门, 深度 3, 保真度 1.0000
  计数: {'00': 33, '01': 496, '10': 318, '11': 153}

保真度基准测试 ​

对于近似方法,可以扫描参数以找到最佳的精度-深度权衡:

python
from pyqpanda3 import core
import numpy as np

np.random.seed(42)
data = np.random.randn(8)
data = (data / np.linalg.norm(data)).tolist()
qubits = [0, 1, 2]

# 精确方法(保真度 = 1.0)
for name, fn in [
    ("amplitude_encode", lambda e: e.amplitude_encode(qubits, data)),
    ("amplitude_encode_recursive", lambda e: e.amplitude_encode_recursive(qubits, data)),
    ("schmidt_encode", lambda e: e.schmidt_encode(qubits, data)),
]:
    enc = core.Encode()
    fn(enc)
    print(f"{name}: fidelity = {enc.get_fidelity(data):.6f}, "
          f"gates = {enc.get_circuit().size()}")

# 不同参数的近似 MPS
for layers in [1, 2, 3, 5, 10]:
    enc = core.Encode()
    enc.approx_mps_encode(qubits, data, layers=layers, sweeps=200)
    print(f"approx_mps_encode (layers={layers}): "
          f"fidelity = {enc.get_fidelity(data):.6f}, "
          f"gates = {enc.get_circuit().size()}")

运行结果:

amplitude_encode: fidelity = 1.000000, gates = 15
amplitude_encode_recursive: fidelity = 1.000000, gates = 5
schmidt_encode: fidelity = 1.000000, gates = 5
approx_mps_encode (layers=1): fidelity = 1.000000, gates = 2
approx_mps_encode (layers=2): fidelity = 1.000000, gates = 4
approx_mps_encode (layers=3): fidelity = 1.000000, gates = 6
approx_mps_encode (layers=5): fidelity = 1.000000, gates = 10
approx_mps_encode (layers=10): fidelity = 1.000000, gates = 20

API 快速参考 ​

Encode 类方法 ​

方法签名描述
basic_encode(qubits, data: str)二进制字符串到基态
angle_encode(qubits, data, gate_type=RY)基于旋转的单量子比特编码
dense_angle_encode(qubits, data)每量子比特两个角度
amplitude_encode(qubits, data)精确振幅(实数/复数)
amplitude_encode_recursive(qubits, data)递归分解
iqp_encode(qubits, data, control_list=[], bool_inverse=False, repeats=1)IQP 线路
schmidt_encode(qubits, data, cutoff=0)Schmidt 分解
dc_amplitude_encode(qubits, data)分治法
bid_amplitude_encode(qubits, data, split=-1)基于分块的编码
ds_quantum_state_preparation(qubits, data)双稀疏(字典/向量)
sparse_isometry(qubits, data)稀疏等距(字典/向量)
efficient_sparse(qubits, data)高效稀疏(字典/向量)
approx_mps_encode(qubits, data, layers=3, sweeps=100, double2float=False)近似 MPS
topology_aware_mps_encode(qubits, data, topology, depth=3)拓扑感知 MPS
get_circuit()返回 QCircuit
get_out_qubits()返回输出量子比特索引
get_fidelity(data)编码保真度

数据类型支持 ​

方法strfloat[]complex[]Dict[str,float]Dict[str,complex]
basic_encode是--------
angle_encode--是------
dense_angle_encode--是------
amplitude_encode--是是----
amplitude_encode_recursive--是是----
iqp_encode--是------
schmidt_encode--是------
dc_amplitude_encode--是------
bid_amplitude_encode--是------
ds_quantum_state_preparation--是是是是
sparse_isometry--是是是是
efficient_sparse--是是是是
approx_mps_encode--是------
topology_aware_mps_encode--是是----

总结 ​

在本教程中你学到了:

  1. 量子态制备将经典数据映射到量子态。你选择的编码方法直接影响线路资源和算法性能。

  2. Encode 类(core.Encode())是统一接口。调用编码方法,然后使用 get_circuit()、get_out_qubits() 和 get_fidelity(data) 提取结果。

  3. 14 种编码方法覆盖了广泛的设计空间:

  4. 保真度评估通过 get_fidelity() 量化编码精度,对于近似方法至关重要。

  5. 方法选择取决于数据类型、精度要求和硬件约束。

下一个教程介绍 哈密顿量与 Pauli 算符,你将学习如何构造和操作用于变分量子算法的哈密顿量。


知识检验 ​

测试你对 pyqpanda3 中量子态制备的理解。

Q1:振幅编码和角度编码有什么区别?你会在什么情况下优先选择其中一种?

A1:振幅编码将数据值直接映射到量子态的概率振幅:|ψ⟩=∑ixi|i⟩(归一化后)。对于 n 个数据值,它使用 ⌈log2⁡(n)⌉ 个量子比特,但产生深度较大的线路。角度编码将每个数据值映射到独立量子比特上的旋转角度:Ry(xi),需要 n 个量子比特表示 n 个值,但产生浅层线路。当量子比特稀缺时优先选择振幅编码,当需要最小化线路深度时优先选择角度编码(例如在 NISQ 硬件上)。

Q2:振幅编码的输入数据必须满足什么归一化条件?如果不满足会怎样?

A2:数据必须满足 ∑i|xi|2=1(单位范数)。amplitude_encode 方法会在内部对输入数据进行归一化,除以总范数。这意味着你可以传递未归一化的数据,方法会自动处理,但你应该注意隐式归一化的存在。

Q3:解释 schmidt_encode 中 cutoff 参数的作用。为什么你可能需要非零的 cutoff?

A3:schmidt_encode 中的 cutoff 参数在 Schmidt 分解中截断低于阈值的奇异值。cutoff=0(默认值)时不进行截断,编码是精确的。非零的 cutoff 会移除较小的奇异值,以降低编码保真度为代价减少线路深度。这种权衡对于精确编码需要太多门的大规模问题非常有用。

Q4:ds_quantum_state_preparation 和 sparse_isometry 有什么区别?两者都处理稀疏数据。

A4:两种方法都针对稀疏量子态,但使用不同的算法。ds_quantum_state_preparation 使用双稀疏方法,针对计算基中具有少量非零振幅的态进行优化。sparse_isometry 使用基于等距的方法,保持内积关系。选择取决于稀疏模式:ds_quantum_state_preparation 通常对均匀稀疏态更高效,而 sparse_isometry 更好地处理结构化稀疏性。

Q5:approx_mps_encode 中的 layers 参数控制什么?增大它会发生什么?

A5:layers 参数控制用于编码的矩阵乘积态(MPS)拟设中的变分层数量。更多的层增加了拟设的表达能力,允许以更高的保真度编码复杂的数据分布。然而,更多的层也会增加线路深度和需要优化的参数数量,需要更多的 sweeps 和可能更长的计算时间。默认的 3 层为大多数用例提供了良好的平衡。

Q6:在 Encode 对象上调用编码方法后,如何在量子程序中使用结果?

A6:在调用编码方法(例如 enc.amplitude_encode(qubits, data))后,使用 enc.get_circuit() 提取线路,使用 enc.get_out_qubits() 获取输出量子比特。然后使用 << 运算符将线路追加到 QProg:prog << enc.get_circuit()。输出量子比特可用于后续操作或测量。

Q7:为什么 approx_mps_encode 有一个 double2float 参数?权衡是什么?

A7:double2float 参数在编码前将双精度(64 位)输入数据转换为单精度(32 位浮点数)。这降低了优化的数值精度,可能导致稍低的保真度但更快的计算速度。权衡是精度与速度。对于大多数量子硬件本身精度有限的应用来说,单精度可能就足够了。


练习 1:编码比较

使用三种不同的方法(角度编码、振幅编码和 Schmidt 编码)编码相同的 4 元素数据向量,然后比较它们的保真度和线路深度。

解答:

python
from pyqpanda3.core import Encode, CPUQVM, QProg, measure

data = [0.5, 0.5, 0.5, 0.5]
qubits = [0, 1]

# --- 角度编码 ---
enc_angle = Encode()
enc_angle.angle_encode(qubits, data[:2])  # Only 2 values for 2 qubits
angle_circuit = enc_angle.get_circuit()
print("Angle encoding circuit:", angle_circuit)

# --- 振幅编码 ---
enc_amp = Encode()
enc_amp.amplitude_encode(qubits, data)
amp_circuit = enc_amp.get_circuit()
amp_fidelity = enc_amp.get_fidelity(data)
print(f"Amplitude encoding fidelity: {amp_fidelity:.6f}")

# --- Schmidt 编码 ---
enc_schmidt = Encode()
enc_schmidt.schmidt_encode(qubits, data, cutoff=0)
schmidt_circuit = enc_schmidt.get_circuit()
schmidt_fidelity = enc_schmidt.get_fidelity(data)
print(f"Schmidt encoding fidelity: {schmidt_fidelity:.6f}")

# --- 运行并比较 ---
qvm = CPUQVM()

for name, circuit in [("Amplitude", amp_circuit), ("Schmidt", schmidt_circuit)]:
    prog = QProg()
    prog << circuit << measure(qubits, [0, 1])
    qvm.run(prog, shots=1000)
    counts = qvm.result().get_counts()
    print(f"{name} encoding results: {counts}")

运行结果:

Angle encoding circuit: 
          ┌──────────────┐ 
q_0:  |0>─┤RY(0.50000000)├ 
          ├──────────────┤ 
q_1:  |0>─┤RY(0.50000000)├ 
          └──────────────┘ 
 c :   / ═════════════════
                          


Amplitude encoding fidelity: 1.000000
Schmidt encoding fidelity: 1.000000
Amplitude encoding results: {'00': 263, '01': 270, '10': 245, '11': 222}
Schmidt encoding results: {'00': 257, '01': 249, '10': 272, '11': 222}

练习 2:稀疏态制备

使用 ds_quantum_state_preparation 和 sparse_isometry 制备一个仅有 2 个非零振幅的 3 量子比特态,比较结果。

解答:

python
from pyqpanda3.core import Encode, CPUQVM, QProg, measure
import numpy as np

# 稀疏态:只有 |000> 和 |111>
sparse_data = {"000": 0.6, "111": 0.8}
qubits = [0, 1, 2, 3, 4, 5]

# 归一化
norm = np.sqrt(sum(v**2 for v in sparse_data.values()))
sparse_data_norm = {k: v/norm for k, v in sparse_data.items()}

# 方法 1:ds_quantum_state_preparation
enc1 = Encode()
enc1.ds_quantum_state_preparation(qubits, sparse_data_norm)
fidelity1 = enc1.get_fidelity(list(sparse_data_norm.values()))
print(f"ds_quantum_state_preparation fidelity: {fidelity1:.6f}")

# 方法 2:sparse_isometry
enc2 = Encode()
enc2.sparse_isometry(qubits, sparse_data_norm)
fidelity2 = enc2.get_fidelity(list(sparse_data_norm.values()))
print(f"sparse_isometry fidelity: {fidelity2:.6f}")

# 在模拟器上运行两种方法
qvm = CPUQVM()

for name, enc in [("DS Preparation", enc1), ("Sparse Isometry", enc2)]:
    prog = QProg()
    prog << enc.get_circuit() << measure(qubits, list(range(len(qubits))))
    qvm.run(prog, shots=1000)
    counts = qvm.result().get_counts()
    print(f"{name}: {counts}")

运行结果:

ds_quantum_state_preparation fidelity: 0.129600
sparse_isometry fidelity: 0.129600
DS Preparation: {'000000': 362, '111000': 638}
Sparse Isometry: {'000000': 356, '000111': 644}

练习 3:近似 MPS 参数扫描

使用 approx_mps_encode 编码 8 元素数据向量,使用不同的 layers(1、3、5)和 sweeps(50、100、200),报告每种组合的保真度。

解答:

python
import numpy as np
from pyqpanda3.core import Encode

# 8 元素数据(需要 3 个量子比特)
data = [0.1, 0.3, 0.5, 0.2, 0.4, 0.6, 0.15, 0.25]
norm = np.linalg.norm(data)
data_norm = [x / norm for x in data]
qubits = [0, 1, 2]

print(f"{'Layers':<8} {'Sweeps':<8} {'Fidelity':<12}")
print("-" * 28)

for layers in [1, 3, 5]:
    for sweeps in [50, 100, 200]:
        enc = Encode()
        enc.approx_mps_encode(qubits, data_norm, layers=layers, sweeps=sweeps)
        fidelity = enc.get_fidelity(data_norm)
        print(f"{layers:<8} {sweeps:<8} {fidelity:.6f}")

运行结果:

Layers   Sweeps   Fidelity    
----------------------------
1        50       1.000000
1        100      1.000000
1        200      1.000000
3        50       1.000000
3        100      1.000000
3        200      1.000000
5        50       1.000000
5        100      1.000000
5        200      1.000000

Released under the MIT License.