在 Quimb 中使用 ArcTN

用 ArcTN 优化 Quimb 的振幅与期望值计算,接入变分算法,并按需加入切片大小限制。

本页目录

接入 ArcTN

将 ArcTNOptimizer 传给 Quimb 的 optimize 参数,即可使用 ArcTN 优化收缩顺序。Quimb 负责构建张量网络,ArcTN 返回包含路径和可选切片信息的 ContractionTree,再由 Quimb/Cotengra 完成数值计算。

安装

安装包含 Light / Heavy 的 ArcTN Python 包后,在同一个虚拟环境中补装 Quimb 接入所需的依赖:

bash
python -m pip install quimb cotengra opt_einsum

ArcTN 的安装方法与环境要求见安装。

计算量子线路的振幅

先构建电路,再在振幅计算中指定 ArcTNOptimizer:

python
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 时的振幅,即 ⟨00⋯0∣U∣00⋯0⟩\langle 00\cdots0|U|00\cdots0\rangle;测量到这个比特串的概率是振幅的模平方。ArcTN 优化的是这个计算所用网络的收缩顺序。

Quimb 的前处理

Quimb 可能先对任务网络做化简,ArcTN 接收的是随后传入的网络。通常保留 Quimb 的默认设置即可;需要关闭其可选化简时,在电路任务调用中设置 simplify_sequence=""。这个参数控制的是 Quimb 传入网络之前的化简。

如果任务已被化简成简单收缩,Quimb/Cotengra 可能直接完成计算,不调用 ArcTN。Quimb 的化简规则见其电路文档。

加入切片大小限制

继续使用上面的电路,将振幅任务转换成张量网络,并设置 target_size:

python
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)用参数化电路 U(θ)U(\theta) 准备状态,并计算哈密顿量的期望值:

H=∑jcjPj,E(θ)=∑jcj⟨0∣U(θ)†PjU(θ)∣0⟩.\begin{aligned} H&=\sum_j c_j P_j,\\ E(\theta)&=\sum_j c_j\langle 0|U(\theta)^\dagger P_j U(\theta)|0\rangle. \end{aligned}

这里 PjP_j 是 Pauli 算符的乘积,cjc_j 是对应系数。Quimb 根据电路和算符构建期望值网络,其中包含电路得到的态 ∣ψ⟩|\psi\rangle、算符 PjP_j 与态的共轭转置 ⟨ψ∣\langle\psi|,不必先生成完整状态向量。Circuit.local_expectation 的 optimize 参数可接收 ArcTNOptimizer。

量子近似优化算法(QAOA)也需要计算期望值,但目标由所求解的组合优化问题决定。两者都可以把每次期望值计算中的收缩序搜索交给 ArcTN。

下面用伪代码说明能量计算与参数优化的关系,不展开具体库的调用。

pseudocode
输入:初始参数 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 的期望值 按问题定义最大化或最小化目标,更新 γ\gamma、β\beta
量子机器学习 所选可观测量的期望值或输出概率 计算模型损失,并更新可训练参数

需要限制中间张量时,可在 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 构建一个四张量网络:

python
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 完成收缩序优化与数值执行:

python
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 处理,不要再次乘入。

相关文档