量子机器学习
概述
unitarylab_algorithms.quantum_machine_learning 包提供适用于近期量子设备(NISQ 时代)的变分和生成量子算法。这些算法使用经典-量子混合优化循环来训练参数化量子线路。
| 算法 | 类 | 任务 |
|---|---|---|
| VQE | VQEAlgorithm | 基态能量估计 |
| VQC | VQCAlgorithm | 量子分类器(鸢尾花数据集) |
| QAOA | QAOAAlgorithm | 组合优化(最大割问题) |
| QCBM | QCBMAlgorithm | 概率分布生成建模(Bars-and-Stripes) |
| CVQNN | CVQNNAlgorithm | 连续变量量子神经网络分类 |
| Fermi-Hubbard VQE | FermiHubbardVQEAlgorithm | 一维开放费米-哈伯德模型基态求解 |
返回与输出
所有算法都返回 status、circuit_path、plot 和 circuit,并附加各自的结果字段。plot 的内容因算法而异:VQE/VQC/CVQNN 各 1 项,QAOA 2 项,QCBM 3 项,Fermi-Hubbard VQE 包含收敛图和参数 .npy 文件。优化质量请结合能量、损失、精度和优化器状态等结果字段判断。
变分量子特征求解器(VQE)
背景
VQE 通过在参数化拟设态上最小化期望值 来估计厄米哈密顿量 的基态能量。该算法在量子化学和材料模拟中被广泛使用。
算法交替执行:
- 在量子线路上计算
- 使用 COBYLA(
scipy.optimize.minimize)更新参数
拟设为 Ry-Rz 环形纠缠线路:每层先对每个量子比特施加 Ry+Rz 旋转,再用一圈 CNOT 纠缠相邻(含首尾回环)量子比特。
导入
from unitarylab_algorithms import VQEAlgorithm.run() 参数
def run(self, n=2, layers=2, max_iter=150, seed=7,
hamiltonian=None, normalize=True,
backend='torch', device='cpu', dtype=np.complex128) -> Dict[str, Any]| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
n | int | 2 | 量子比特数(仅在 hamiltonian=None 时生效) |
layers | int | 2 | 拟设中的变分层数 |
max_iter | int | 150 | COBYLA 最大迭代次数 |
seed | int | 7 | 随机哈密顿量生成与初始参数采样的随机种子 |
hamiltonian | np.ndarray | None | None | 自定义厄米哈密顿量;为 None 时内部生成随机厄米矩阵 |
normalize | bool | True | 生成随机哈密顿量时是否按谱范数归一化(仅影响随机哈密顿量分支) |
_validate_hamiltonian() 会校验矩阵是方阵、维数为 2 的幂、且满足 ,否则抛出 ValueError。
返回值
{
"status": "ok",
"circuit_path": "<VQE_Circuit.svg.svg 路径>",
"plot": [{"format": "svg", "filename": "<VQE_Convergence.svg 路径>"}],
"circuit": <Circuit 对象>,
"Exact Energy": ...,
"VQE Energy": ...,
"Absolute Error": ...,
"Optimizer Message": "...",
"Quantum Comp Time": ...,
}示例
import numpy as np
from unitarylab_algorithms import VQEAlgorithm
# 使用自定义哈密顿量
H = np.array([[1, 0.5], [0.5, -1]])
algo = VQEAlgorithm()
result = algo.run(n=2, layers=2, max_iter=150, hamiltonian=H)
print(result['status'], result['VQE Energy'], result['Absolute Error'])快速演示
from unitarylab_algorithms.quantum_machine_learning.vqe.algorithm import test
test(n=2, layers=2, max_iter=150)注意事项
- 传入自定义哈密顿量时
n会被静默覆盖:若hamiltonian非None,代码会先校验并从矩阵维数推出真实量子比特数,然后执行n = num_qubits,用户传入的n参数会被忽略而不会报错或警告—— normalize参数只在hamiltonian=None(内部随机生成分支)时才会被使用;若传入自定义哈密顿量,normalize不产生任何效果。parameters.json中的description字段仍写着”寻找 2 量子比特 Ising 哈密顿量 H = Z0Z1 - 0.5X0 - 0.5X1 的基态能量”,但run()的默认行为其实是生成随机厄米矩阵(由seed决定),并非该固定 Ising 哈密顿量——parameters.json描述已过期,不能作为默认行为的依据。- UI 默认值与 Python 默认值不一致:
parameters.json中layers的默认值为3,而run()签名的默认值为layers=2;通过 Web UI 提交空表单与直接调用VQEAlgorithm().run()得到的层数并不相同。 status恒为'ok'(见包级提示),应改用Optimizer Message判断 COBYLA 是否真正收敛。
变分量子分类器(VQC)
背景
VQC 通过角度编码将经典特征映射到量子态中,然后用可训练拟设加 Pauli-Z 期望值观测量进行 3 分类。本实现固定应用于鸢尾花数据集(4 个特征、3 个类别),梯度通过手动实现的**参数漂移规则(Parameter Shift Rule)**逐参数计算(并非自动微分),使用 CrossEntropyLoss(对 10 倍缩放后的期望值 logits)训练。
导入
from unitarylab_algorithms import VQCAlgorithm.run() 参数
def run(self, layers=3, epochs=20, lr=0.05, batch_size=16,
backend='torch', device='cpu', dtype=np.complex128) -> Dict[str, Any]| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
layers | int | 3 | 变分层数 |
epochs | int | 20 | 训练轮数(对完整训练集的遍历次数) |
lr | float | 0.05 | Adam 优化器学习率 |
batch_size | int | 16 | 小批量大小 |
量子比特数固定为 4(对应鸢尾花数据集的 4 个特征),不作为参数暴露。
返回值
{
"status": "ok",
"circuit_path": "<vqc_algorithm_circuit.svg 路径>",
"plot": [{"format": "svg", "filename": "<VQC_Metrics.svg 路径>"}],
"circuit": <Circuit 对象>,
"Final Loss": ...,
"Final Accuracy": ...,
"Quantal Computation Time (s)": ...,
}示例
from unitarylab_algorithms import VQCAlgorithm
algo = VQCAlgorithm()
result = algo.run(layers=3, epochs=20, lr=0.05, batch_size=16)
print(result['status'], result['Final Accuracy'])快速演示
from unitarylab_algorithms.quantum_machine_learning.vqc.algorithm import test
test(layers=3, epochs=20, lr=0.05, batch_size=16)注意事项
- 返回字典中的输出键存在拼写问题:真实的输出键名为
"Quantal Computation Time (s)"(应为 “Quantum”),这是源码中的原样拼写,调用方按字段名取值时请使用这个确切(带拼写误差的)字符串,而非语义上”正确”的"Quantum Computation Time (s)"。 - 量子比特数固定为 4(由鸢尾花数据集的特征数决定),无法通过参数调整为其他数据集。
_load_iris_data()优先使用sklearn.datasets.load_iris;若环境中未安装scikit-learn,会退回到源码文件内嵌的鸢尾花数据固定副本(120 训练 / 30 测试样本),因此即使没有scikit-learn依赖算法也能运行,但退回路径下无法自定义数据集划分比例(内嵌数据固定 80/20 划分)。- 参数漂移梯度通过双重
for循环对theta的每个分量分别做前向/后向偏移评估(共 次线路执行/参数更新),随层数和批大小增长计算开销线性上升。 __init__中固定调用torch.manual_seed(42)与np.random.seed(42),没有暴露seed参数。正常的“构造实例后立即调用run()”路径中,初始参数与数据集划分均可复现——虽然train_test_split未显式传入random_state,但它会使用刚被重置为 42 的 NumPy 全局随机状态;若在构造实例后、调用run()前有其他代码消耗该全局随机状态,数据集划分仍可能变化。因此当前实现依赖全局随机状态,可复现但不具备隔离性,且用户无法外部调整种子。parameters.json中layers默认值为5,而run()签名默认值为layers=3——同样存在 UI/Python 默认值不一致的情况。status恒为'ok'(见包级提示)。
量子近似优化算法(QAOA)
背景
QAOA 是一种用于组合优化问题的变分算法。它交替应用编码目标函数的问题哈密顿量 (由图的每条边对应的 项构成)和由 旋转实现的混合层,共执行 layers 轮。本实现求解由边列表定义的图上的最大割问题,通过 COBYLA 优化 。
导入
from unitarylab_algorithms import QAOAAlgorithm.run() 参数
def run(self, edges=None, n=6, layers=4, max_iter=100,
backend='torch', device='cpu', dtype=np.complex128) -> Dict[str, Any]| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
edges | List[Tuple[int,int]] | None | None | 图的边列表;为 None 时使用内置默认图 [(0,1),(1,2),(2,3),(3,0),(0,4),(1,5)] |
n | int | 6 | 量子比特数(图的顶点数) |
layers | int | 4 | QAOA 演化层数 |
max_iter | int | 100 | COBYLA 最大迭代次数 |
返回值
{
"status": "ok",
"circuit_path": "<QAOA_Circuit.svg 路径>",
"plot": [
{"format": "svg", "filename": "<QAOA_Convergence.svg 路径>"},
{"format": "svg", "filename": "<MaxCut_Solution.svg 路径>"},
],
"circuit": <Circuit 对象>,
"Optimal bitstring": "...",
"Max-Cut Value": ...,
"Optimized Energy": ...,
"Quantum Computation Time": ...,
}示例
from unitarylab_algorithms import QAOAAlgorithm
edges = [(0, 1), (1, 2), (2, 3), (3, 0), (0, 4), (1, 5)]
algo = QAOAAlgorithm()
result = algo.run(edges=edges, n=6, layers=4, max_iter=100)
print(result['status'], result['Optimal bitstring'], result['Max-Cut Value'])快速演示
from unitarylab_algorithms.quantum_machine_learning.qaoa.algorithm import test
test()注意事项
QAOAAlgorithm.__init__中固定调用np.random.seed(42)与torch.manual_seed(42),未暴露seed参数;初始参数initial_params = np.random.uniform(0, np.pi, 2*layers)依赖该全局种子。plot字段包含 2 个条目(收敛曲线图 + 最大割结果图),调用方遍历plot时不应只取第一个元素。- 模块级
test()函数自身默认max_iter=60,与.run()方法签名的默认值max_iter=100不一致;而文件底部if __name__ == "__main__":块中又显式使用max_iter=100。三处默认值并不统一,请以你实际调用的入口(.run()还是test())核对使用的默认值。 status恒为'ok'(见包级提示),应结合Optimized Energy与已知的图的精确最大割值比较来判断质量。
量子线路玻恩机(QCBM)
背景
QCBM 将参数化量子线路用作生成模型,训练 Born 概率分布 拟合目标分布。本实现的目标分布固定为 Bars-and-Stripes(BAS,条纹图案)分布,由 _get_bas_dist(n) 在内部计算生成(依据 自动推出最接近方形的 rows × cols 网格,枚举全行相同或全列相同的二进制模式作为”有效”状态,均匀赋予非零概率),不支持用户传入任意目标分布。损失函数为 KL 散度(torch.sum(target * log((target+eps)/(curr+eps)))),通过参数漂移规则计算梯度。
导入
from unitarylab_algorithms import QCBMAlgorithm.run() 参数
def run(self, n=4, layers=4, epochs=40, lr=0.1,
backend='torch', device='cpu', dtype=np.complex128) -> Dict[str, Any]| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
n | int | 4 | 量子比特数 |
layers | int | 4 | 变分线路深度 |
epochs | int | 40 | 训练迭代次数 |
lr | float | 0.1 | Adam 优化器学习率 |
_validate_run_params() 会显式校验 n/layers/epochs 为正整数、lr 为正数,不满足时抛出 ValueError——这是本包中少数在 Python 层面做了真正入参校验的算法。
返回值
{
"status": "ok",
"circuit_path": "<qcbm_algorithm_circuit.svg 路径>",
"plot": [
{"format": "svg", "filename": "<QCBM_Loss.svg 路径>"},
{"format": "svg", "filename": "<QCBM_Distribution.svg 路径>"},
{"format": "svg", "filename": "<QCBM_Samples.svg 路径>"},
],
"circuit": <Circuit 对象>,
"Final KL Loss": ...,
"Quantum Computation Time": ...,
}示例
from unitarylab_algorithms import QCBMAlgorithm
algo = QCBMAlgorithm()
result = algo.run(n=4, layers=4, epochs=40, lr=0.1)
print(result['status'], result['Final KL Loss'])快速演示
from unitarylab_algorithms.quantum_machine_learning.qcbm.algorithm import test
test(n=4, layers=4, epochs=40, lr=0.1)注意事项
- 目标分布固定为 BAS(Bars-and-Stripes)分布,由
n通过_get_bas_dist(n)生成,不支持传入自定义目标分布。 - 损失函数使用 KL 散度(见上方公式)。
plot字段包含 3 个条目(损失曲线、分布对比图、采样网格图)。QCBMAlgorithm.__init__固定调用torch.manual_seed(42)/np.random.seed(42),未暴露seed参数。status恒为'ok'(见包级提示),但本算法是本包中少数在入口处对参数做了真正校验(_validate_run_params)的算法,非法入参会直接抛出ValueError而不是静默产生错误结果。
连续变量量子神经网络(CVQNN)
背景
CVQNN 在**连续变量(CV)**量子计算范式下工作,使用光学模式而非量子比特,在截断 Fock 空间(维数为 cutoff)中通过位移(displacement)、压缩(squeezing)、分束器(beamsplitter)、旋转(rotation)和克尔非线性(Kerr)等算符(均以 torch.matrix_exp 实现)构造变分线路,训练用于二分类(MSELoss,标签映射到 )。
导入
from unitarylab_algorithms import CVQNNAlgorithm.run() 参数
def run(self, x_train: np.ndarray, y_train: np.ndarray,
n_layers: int = 2, cutoff: int = 6, epochs: int = 40,
lr: float = 0.05) -> Dict[str, Any]| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
x_train | np.ndarray | — (必填) | 输入特征数组,形状 (N, 2) |
y_train | np.ndarray | — (必填) | 标签数组(0/1) |
n_layers | int | 2 | 变分 CV 层数 |
cutoff | int | 6 | Fock 空间截断维数 |
epochs | int | 40 | 训练轮数 |
lr | float | 0.05 | Adam 优化器学习率 |
注意:与本包其他算法不同,CVQNNAlgorithm.run() 没有 backend/device/dtype 参数——CV 模拟完全由自定义的 CVSimulator(基于 torch.matrix_exp 的稠密矩阵指数)实现,不经过 Circuit.execute() 后端调度。
返回值
{
"status": "ok",
"circuit_path": "<cvqnn_algorithm_circuit.svg 路径>",
"plot": [{"format": "svg", "filename": "<CVQNN_Metrics.svg 路径>"}],
"circuit": <Circuit 对象>,
"Final Loss": ...,
"Final Accuracy": ...,
"Total Computation Time (s)": ...,
}示例
import numpy as np
from unitarylab_algorithms import CVQNNAlgorithm
# 生成简单的二维数据集
x_train = np.random.randn(50, 2)
y_train = (x_train[:, 0] + x_train[:, 1] > 0).astype(int)
algo = CVQNNAlgorithm()
result = algo.run(x_train=x_train, y_train=y_train, n_layers=2, cutoff=6, epochs=30)
print(result['status'], result['Final Accuracy'])快速演示
from unitarylab_algorithms.quantum_machine_learning.cvqnn.algorithm import test
test(n_layers=2, cutoff=6, epochs=30, lr=0.05)注意事项
- 保存的电路图并不反映训练后的门参数:
save_circuit()所绘制的Circuit来自_build_circuit(),该方法仅用qc.ry(0.0, ...)/qc.rx(0.0, ...)/qc.rz(0.0, ...)等占位角度恒为 0 的门搭建拓扑用于展示结构,真正参与训练与推理的连续变量参数(sq_r、disp_r、rot_theta、kerr_k、bs_theta)存在于独立的CVClassifier/CVSimulator(基于原始torch张量运算)中,并不写回到导出的电路图上。因此circuit_path对应的 SVG 只能体现线路结构,不能体现训练得到的实际参数值。 - 增大
cutoff可以提升近似精度,但内存占用(cutoff × cutoff稠密矩阵指数)随之增长,标准硬件上不建议超过12(parameters.json中 UI 上限即为12)。 - CV 框架对光学量子系统建模,与基于量子比特的线路在本质上不同,
x_op、n_op等算符定义在截断 Fock 基下,cutoff过小会引入截断误差。 CVQNNAlgorithm.__init__固定调用torch.manual_seed(42)、np.random.seed(42)并设置torch.set_default_dtype(torch.float64),未暴露seed参数。- 模块级
test()函数自身默认epochs=30,与.run()方法签名默认值epochs=40不一致;test()内部数据集优先用sklearn.datasets.make_moons生成,若不可用则退回到源码内嵌的固定副本(40 个样本)。 status恒为'ok'(见包级提示)。
费米-哈伯德模型 VQE(Fermi-Hubbard VQE)
FermiHubbardVQEAlgorithm 位于 fermi_hubbard_vqe/algorithm.py。
背景
该算法针对一维开放边界费米-哈伯德模型(L 个格点,最近邻跳跃系数 t、同格点相互作用强度 U、塞曼磁场系数 B):
- 通过
unitarylab.library.fermi_hubbard.fermi_hubbard_pauli模块构建费米子哈密顿量及其经 Jordan-Wigner 变换后的 Pauli 字符串表达式; - 用
pauli_ground_state()做稠密精确对角化,获得精确基态能量作为参照; - 内部复用
VQEAlgorithm(见上文”变分量子特征求解器”一节)执行 VQE 优化,求得该 Pauli 哈密顿量的近似基态能量; - 可选地对优化后线路测量总自旋磁矩。
端序(Endianness)适配
fermi_hubbard_pauli 生成的 Pauli 表达式采用的量子比特编号约定,与 UnitaryLab 线路模拟器的最低位比特(q0)为最低有效位约定相反。因此源码在把 Pauli 哈密顿量矩阵交给 VQEAlgorithm 之前,会调用内部的 _bit_reverse_hamiltonian() 对矩阵的行列做比特翻转置换,并做以下自检(任一项不满足即抛出异常,不会静默产生错误结果):
- 往返一致性:翻转两次应还原原矩阵,误差需
< 1e-12(否则抛出ValueError); - 谱一致性:翻转前后矩阵的特征值谱应一致,误差需
< 1e-10(否则抛出ValueError); - 环境自检
_check_unitarylab_environment():进程内首次调用时执行Circuit(2).x(0)并检查末态峰值确实落在索引1(即验证 q0 是最低有效位这一假设本身成立),若当前 UnitaryLab 版本的比特序约定发生变化则抛出RuntimeError。
导入
from unitarylab_algorithms import FermiHubbardVQEAlgorithm.run() 参数
def run(self, params: Dict[str, Any] | str | None = None, *,
L: int = 2, t: float = 1.0, U: float = 4.0, B: float = 1.5,
layers: int = 5, max_iter: int = 1000, seed: int = 7,
measure_shots: int = 10000,
backend: str = "torch", device: str = "cpu",
dtype = np.complex128) -> Dict[str, Any]| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
params | dict | str | None | None | 可选:一次性以字典或 JSON 字符串传入下列关键字参数(详见下方说明),覆盖对应的关键字默认值 |
L | int | 2 | 一维格点数(开放边界) |
t | float | 1.0 | 最近邻跳跃系数 |
U | float | 4.0 | 同格点相互作用强度 |
B | float | 1.5 | 塞曼磁场系数 |
layers | int | 5 | VQE 拟设变分层数 |
max_iter | int | 1000 | COBYLA 最大迭代次数 |
seed | int | 7 | VQE 初始参数随机种子 |
measure_shots | int | 10000 | 优化后线路测量总自旋磁矩的采样数;设为 0 可跳过该测量阶段 |
除 params 外,其余参数均为仅限关键字参数(签名中 * 之后)。若提供 params,其中的键会覆盖对应关键字的默认值;layers/max_iter/measure_shots 还分别兼容别名键 vqe_layers/vqe_max_iter/measurement_shots。
返回值
{
"status": "ok",
"circuit_path": "<Fermi_Hubbard_VQE_Circuit.svg 路径>",
"plot": [
{"format": "svg", "filename": "<Fermi_Hubbard_VQE_Convergence.svg 路径>"},
{"format": "npy", "filename": "<Fermi_Hubbard_VQE_Parameters.npy 路径>"},
],
"circuit": <Circuit 对象>,
"Exact Energy": ...,
"VQE Energy": ...,
"Absolute Error": ...,
"Circuit Energy": ...,
"Number of Qubits": ...,
"Optimizer Evaluations": ...,
"Optimizer Converged": ...,
"Optimizer Message": "...",
"VQE Runtime": ...,
"Total Runtime": ...,
"Qubit Mapping": ...,
"Fermionic Hamiltonian": "...",
"Pauli Hamiltonian": "...",
# 仅当 measure_shots > 0 时追加:
"Measured Magnetic Moment": ...,
"Magnetic Moment Standard Errors": ...,
"Measurement Total Shots": ...,
}示例
from unitarylab_algorithms import FermiHubbardVQEAlgorithm
algo = FermiHubbardVQEAlgorithm()
result = algo.run(L=2, t=1.0, U=4.0, B=1.5, layers=5, max_iter=1000, measure_shots=10000)
print(result['status'], result['VQE Energy'], result['Exact Energy'])也可以用一个参数字典整体传入(与 params=None 时逐关键字传参等价):
algo = FermiHubbardVQEAlgorithm()
result = algo.run(params={"L": 3, "U": 6.0, "vqe_layers": 6})快速演示
from unitarylab_algorithms.quantum_machine_learning.fermi_hubbard_vqe.algorithm import test
test(L=2, t=1.0, U=4.0, B=1.5, layers=5, max_iter=1000, seed=7, measurement_shots=10000)注意事项
- 最佳观测参数追踪:源码内部用
_TrackingVQEAlgorithm(VQEAlgorithm的子类)重写_expectation(),在 COBYLA 每次目标函数求值时记录能量历史与迄今为止能量最低的参数组合(best_energy/best_parameters),因为 COBYLA 收敛时的最终迭代点未必是历史最优点——最终返回的VQE Energy/Circuit Energy来自这个”历史最优”,而非优化器返回的最后一次迭代。 - 多重正确性自检、失败即抛异常:除端序自检外,
run_pauli_vqe()还会检查:VQE 内部调用是否报告status == 'ok'(否则RuntimeError)、是否发生过零次目标函数求值(否则RuntimeError)、best_parameters是否存在且能量有限(否则RuntimeError)、VQE 报告的精确能量是否与 Pauli 哈密顿量谱一致(误差< 1e-10,否则ValueError)、重新在优化线路上计算的能量是否与记录的最优能量一致(误差< 1e-8,否则RuntimeError)、以及最优能量是否违反变分法下界(< 精确能量 - 1e-8则ValueError,提示可能是端序问题)。因此该算法一旦正常返回(不抛异常),其数值一致性比包内其他算法更有保障;但这也意味着它比其他算法更容易在输入或环境异常时直接抛出异常终止,而非返回一个status: 'failed'的结果字典。 params与逐关键字参数可以混用,但params中的键始终优先覆盖对应关键字的默认值(不会覆盖你显式传入的同名关键字实参之外的其他关键字)。plot字段的第二项是.npy文件(优化后 VQE 参数的 NumPy 数组),并非图片,前端渲染或下载逻辑需要按format字段区分处理,不能假设plot列表内全部是可直接显示的图像。measure_shots=0时会跳过磁矩测量阶段,返回字典中不会包含Measured Magnetic Moment等三个测量相关键——调用方应先判断这些键是否存在,而非假定其总是出现。
版本引用
from unitarylab_algorithms import (
VQEAlgorithm,
VQCAlgorithm,
QAOAAlgorithm,
QCBMAlgorithm,
CVQNNAlgorithm,
FermiHubbardVQEAlgorithm,
)