在 Quimb 中使用 ArcTN
用 ArcTN 优化 Quimb 的振幅与期望值计算,接入变分算法,并按需加入切片大小限制。
本页目录
接入 ArcTN
将 ArcTNOptimizer 传给 Quimb 的 optimize 参数,即可使用 ArcTN 优化收缩顺序。Quimb 负责构建张量网络,ArcTN 返回包含路径和可选切片信息的 ContractionTree,再由 Quimb/Cotengra 完成数值计算。
安装
安装包含 Light / Heavy 的 ArcTN Python 包后,在同一个虚拟环境中补装 Quimb 接入所需的依赖:
python -m pip install quimb cotengra opt_einsum
ArcTN 的安装方法与环境要求见安装。
计算量子线路的振幅
先构建电路,再在振幅计算中指定 ArcTNOptimizer:
import numpy as np
import quimb.tensor as qtn
from arctn import ArcTNOptimizer
n = 8
rng = np.random.default_rng(0)
circ = qtn.Circuit(n)
for depth in range(6):
for q in range(n):
circ.apply_gate("RY", float(rng.uniform(0, 2 * np.pi)), q)
for q in range(depth % 2, n - 1, 2):
circ.apply_gate("CNOT", q, q + 1)
optimizer = ArcTNOptimizer(preset="light", seed=0)
amplitude = circ.amplitude("0" * n, optimize=optimizer)
print(amplitude)
输出是电路从全零初态出发,最终比特串也全部为 0 时的振幅,即 ;测量到这个比特串的概率是振幅的模平方。ArcTN 优化的是这个计算所用网络的收缩顺序。
Quimb 的前处理
Quimb 可能先对任务网络做化简,ArcTN 接收的是随后传入的网络。通常保留 Quimb 的默认设置即可;需要关闭其可选化简时,在电路任务调用中设置 simplify_sequence=""。这个参数控制的是 Quimb 传入网络之前的化简。
如果任务已被化简成简单收缩,Quimb/Cotengra 可能直接完成计算,不调用 ArcTN。Quimb 的化简规则见其电路文档。
加入切片大小限制
继续使用上面的电路,将振幅任务转换成张量网络,并设置 target_size:
tn = circ.amplitude_tn("0" * n)
optimizer = ArcTNOptimizer(
preset="heavy", seed=0,
target_size=2**28,
slicing_mode="fixed",
)
value = tn.contract(
all, output_inds=(), optimize=optimizer,
)
Quimb/Cotengra 通过 ArcTNOptimizer.search() 获取包含路径和切片 index 的收缩树,再计算各切片并求和。fixed 保持搜索得到的路径不变;dynamic 允许局部调整路径,区别见固定路径切片与动态切片。
每片最大二元收缩结果的元素数不能超过 target_size;它不是总内存上限。若原路径已经满足限制,可以不切片。
只有一两个输入张量的简单收缩可能绕过外部优化器。若这类网络也必须检查 target_size,直接使用 arctn_tree 或 arctn_contract。
VQE 与 QAOA 的期望值计算
变分量子本征求解器(VQE)用参数化电路 准备状态,并计算哈密顿量的期望值:
这里 是 Pauli 算符的乘积, 是对应系数。Quimb 根据电路和算符构建期望值网络,其中包含电路得到的态 、算符 与态的共轭转置 ,不必先生成完整状态向量。Circuit.local_expectation 的 optimize 参数可接收 ArcTNOptimizer。
量子近似优化算法(QAOA)也需要计算期望值,但目标由所求解的组合优化问题决定。两者都可以把每次期望值计算中的收缩序搜索交给 ArcTN。
下面用伪代码说明能量计算与参数优化的关系,不展开具体库的调用。
输入:初始参数 theta_initial,哈密顿量各项 (c_j, P_j)
function energy(theta):
circuit = 用 theta 构建参数化电路
value = 0
对哈密顿量中的每一项 (c_j, P_j):
network = 根据 circuit 和 P_j 构建期望值张量网络
tree = 用 ArcTN 优化 network 的收缩序,按需加入切片
expectation = 沿 tree 收缩 network;有切片时求和
value = value + c_j * expectation
return value 的实部
theta_star = 从 theta_initial 出发,用经典优化算法最小化 energy
实际使用 Quimb 时,向 Circuit.local_expectation 的 optimize 参数传入 ArcTNOptimizer,即可完成伪代码中的网络构建、收缩序优化与数值计算。经典优化算法由应用另行调用;ArcTN 选择收缩序,不更新电路参数。这里直接计算期望值,不使用有限次测量采样(shots)。
| 应用 | 电路计算返回什么 | 外层算法做什么 |
|---|---|---|
| VQE | 哈密顿量各项的期望值 | 加权求和,并最小化能量 |
| QAOA | 问题对应的 cost Hamiltonian 的期望值 | 按问题定义最大化或最小化目标,更新 、 |
| 量子机器学习 | 所选可观测量的期望值或输出概率 | 计算模型损失,并更新可训练参数 |
需要限制中间张量时,可在 ArcTNOptimizer 中加入 target_size=2**20, slicing_mode="fixed",其含义与上面的切片示例相同。Quimb/Cotengra 负责执行返回的切片;该设置不是进程总内存上限。
同一个 ArcTNOptimizer 对象不缓存收缩序。是否复用由调用方决定;对于结构固定的参数化网络,可显式保存收缩序并重复执行,见参数变化时复用收缩序。不同可观测量可能对应不同网络,不能直接共用一条路径。
参数梯度与其他前端
ArcTN 的收缩序优化不负责计算电路参数的梯度。无梯度优化可以直接使用上述能量函数;需要梯度时,可对满足条件的门使用参数移位规则(parameter-shift rule),在移位后的参数处计算期望值,再组合得到梯度;也可以使用前端支持的自动微分。原生 Rust 数值执行接口不提供参数梯度,使用外部数组后端时则需确认实际调用的运算支持求导。Quimb 的自动微分用法见其优化文档。
其他电路前端需要先将计算任务转换为张量网络。若调用方使用 Quimb 或 opt_einsum 的收缩接口,可以复用已有接入;若要求特定框架的设备或执行接口,则需要相应适配。ArcTNOptimizer 本身不是 Qiskit 的 Estimator、Sampler,也不是 PennyLane 的 device。
构建张量网络
用数组和 index 构建一个四张量网络:
import numpy as np
import quimb.tensor as qtn
from arctn import ArcTNOptimizer
rng = np.random.default_rng(0)
ta = qtn.Tensor(rng.normal(size=(2, 3)), inds=("a", "x"))
tb = qtn.Tensor(rng.normal(size=(3, 4)), inds=("x", "y"))
tc = qtn.Tensor(rng.normal(size=(4, 5)), inds=("y", "z"))
td = qtn.Tensor(rng.normal(size=(5, 2)), inds=("z", "b"))
tn = qtn.TensorNetwork([ta, tb, tc, td])
result = tn.contract(
all, output_inds=("a", "b"),
optimize=ArcTNOptimizer(preset="light", seed=0),
)
print(result.data.shape) # (2, 2)
相同的 index 名表示连接,其维度必须一致。output_inds=("a", "b") 保留输出的两个 index,并规定结果轴的顺序;标量结果使用 output_inds=()。网络输入要求见张量网络与收缩路径。
用 ArcTN 执行收缩
也可以从 Quimb 网络提取结构和数组,交给 arctn_contract 完成收缩序优化与数值执行:
from arctn import arctn_contract
tensors = list(tn)
value = arctn_contract(
[tuple(t.inds) for t in tensors],
("a", "b"),
dict(tn.ind_sizes()),
[t.data for t in tensors],
preset="heavy", backend="numpy",
)
value = value * (10.0 ** float(tn.exponent))
backend="numpy" 使用 NumPy 数组后端;也可选择 native,见执行后端。若 Quimb 将缩放因子单独记录在 tn.exponent 中,提取数组后需要乘回 10**tn.exponent,如上例最后一行;没有单独缩放时,该指数为零。通过 tn.contract 计算时由 Quimb 处理,不要再次乘入。
相关文档
- Python API:
ArcTNOptimizer的参数与返回值。 - 第三方工具接入:路径接口与收缩树接口的区别。
- Quimb Tensor Contraction:Quimb 的张量网络收缩接口。
- Quimb 的期望值计算:
Circuit.local_expectation的算符与输出。 - 计时与 CPU 使用量:区分收缩序优化时间和完整计算时间。