qml.渐变

量子梯度变换是计算量子电路梯度的策略,通过变换量子电路为一个或多个梯度电路来实现。这些梯度电路在执行和后处理后,会返回原始电路的梯度。

量子梯度变换的例子包括有限差分和参数移位规则。

此模块提供了一组与设备无关的、可微分的量子梯度变换。因此,这些量子梯度变换可以用于在模拟器和硬件上计算量子电路的梯度。

此外,它还包括一个API,用于编写您自己的量子梯度变换。

这些量子梯度变换可以以两种方式使用:

  • 直接转换量子电路

  • 注册一个量子梯度策略,以便在使用QNode进行自动微分时使用。

概述

梯度变换

finite_diff(tape[, argnum, h, approx_order, ...])

将电路转化为计算所有门参数相对于其输入的有限差分梯度。

param_shift(tape[, argnum, shifts, ...])

将电路转换为计算所有门参数相对于其输入的参数移位梯度。

param_shift_cv(tape, dev[, argnum, shifts, ...])

将连续变量 QNode 转换为计算所有门参数相对于其输入的参数偏移梯度。

param_shift_hessian(tape[, argnum, ...])

转换电路以计算相对于其可训练参数的参数偏移海森矩阵。

spsa_grad(tape[, argnum, h, approx_order, ...])

将电路转换为计算所有门参数相对于其输入的SPSA梯度。

hadamard_grad(tape[, argnum, aux_wire, ...])

转换电路以计算所有门相对于其输入的哈达玛测试梯度。

stoch_pulse_grad(tape[, argnum, ...])

通过应用随机参数移位规则,计算由脉冲序列组成的量子电路的梯度。

pulse_odegen(tape[, argnum, atol])

转换电路以计算脉冲程序中脉冲相对于其输入的脉冲发生器参数偏移梯度。

度量张量

metric_tensor(tape[, argnum, approx, ...])

返回一个计算给定QNode或量子录音带的度量张量的函数。

adjoint_metric_tensor(tape)

实现Jones中概述的伴随方法以计算度量张量。

工具函数

finite_diff_coeffs(n, 约定顺序, 策略)

生成给定导数阶数、近似精度和策略的有限差分移位值及相应的项系数。

generate_shifted_tapes(tape, index, shifts)

生成一个胶带的列表或一个单一的广播胶带,其中一个标记的可训练参数已根据提供的偏移值进行移位。

generate_multishifted_tapes(tape, indices, ...)

生成一个列表,其中多个标记的可训练参数已按照提供的偏移值进行了偏移。

generate_shift_rule(频率[, 移位, 顺序])

计算基于其生成元特征值频谱的单位的参数偏移规则。

generate_multi_shift_rule(频率[, ...])

计算相对于两个参数化单位的参数变化规则,给定其产生者的特征值频率谱。

eigvals_to_frequencies(eigvals)

将特征值谱转换为频率值,定义为谱中特征值的正的、唯一的差值集合。

compute_vjp_single(dy, jac[, num])

便捷函数,用于计算给定梯度输出向量和单个测量带的雅可比矩阵的向量-雅可比乘积。

compute_vjp_multi(dy, jac[, num])

便捷函数,用于计算给定梯度输出向量和具有多个测量的雅可比矩阵的向量-雅可比积。

batch_vjp(tapes, dys, gradient_fn[, ...])

生成计算一批梯度带的向量-雅可比乘积所需的梯度带和处理函数。

vjp(tape, dy, gradient_fn[, gradient_kwargs])

生成计算带的向量-雅可比乘积所需的梯度带和处理函数。

compute_jvp_single(切向量, 雅可比)

方便的函数,用于计算给定切向量和单个测量带的雅可比矩阵向量积。

compute_jvp_multi(切向量, 雅可比)

方便函数,用于计算给定梯度输出向量和具有多个测量值的 tape 的雅可比-向量积。

batch_jvp(tapes, tangents, gradient_fn[, ...])

生成所需的梯度带和处理函数,以计算一组梯度带的雅可比向量积。

jvp(tape, tangent, gradient_fn[, ...])

生成计算带的雅可比向量积所需的梯度带和处理函数。

classical_jacobian(qnode[, argnum, ...])

返回一个函数,用于提取QNode经典部分的雅可比矩阵。

classical_fisher(qnode[, argnums])

返回一个计算给定 QNode 或量子带的经典费舍尔信息矩阵(CFIM)的函数。

quantum_fisher(tape, device, *args, **kwargs)

返回一个函数,用于计算给定QNode的量子费舍尔信息矩阵(QFIM)。

注册自动微分梯度

所有PennyLane QNodes都是自动可微分的,可以无缝地包含在自动微分管道中。当创建一个 QNode 时,确定最佳微分策略的策略是 自动化 的,并考虑电路、设备、自动微分框架和元数据(例如,是否使用有限数量的拍摄)。

dev = qml.device("default.qubit", shots=1000)

@qml.qnode(dev, interface="tf")
def circuit(weights):
    ...

特别是:

  • 当使用具有准确测量统计的模拟器设备时,由于性能和内存的改进,推荐使用反向传播。

  • 当使用硬件设备或具有有限次数实验的模拟器时,优先使用量子梯度变换,例如参数位移规则。

如果您希望在对量子电路进行微分时指定特定的量子梯度变换,可以在创建 QNode 时传递该变换:

@qml.qnode(dev, diff_method=qml.gradients.param_shift)
def circuit(weights):
    ...

当使用您首选的自动微分框架计算混合量子-经典成本函数的梯度时,将使用为每个QNode指定的梯度变换。

注意

单个成本函数可以包含多个QNode,每个QNode都有自己的量子梯度变换注册。

转换QNodes

或者,可以手动将量子梯度变换应用于QNodes。这并不推荐,因为PennyLane必须计算参数的经典雅可比矩阵并将其与量子雅可比矩阵相乘,我们建议使用diff_method关键字参数与您喜欢的机器学习框架。

dev = qml.device("default.qubit")

@qml.qnode(dev)
def circuit(weights):
    qml.RX(weights[0], wires=0)
    qml.RY(weights[1], wires=1)
    qml.CNOT(wires=[0, 1])
    qml.RX(weights[2], wires=1)
    return qml.probs(wires=1)
>>> weights = np.array([0.1, 0.2, 0.3], requires_grad=True)
>>> circuit(weights)
tensor([0.9658079, 0.0341921], requires_grad=True)
>>> qml.gradients.param_shift(circuit)(weights)
tensor([[-0.04673668, -0.09442394, -0.14409127],
        [ 0.04673668,  0.09442394,  0.14409127]], requires_grad=True)

将这与自动微分进行比较:

>>> qml.jacobian(circuit)(weights)
array([[-0.04673668, -0.09442394, -0.14409127],
       [ 0.04673668,  0.09442394,  0.14409127]])

量子梯度变换也可以作为装饰器应用于QNodes,如果仅仅需要梯度信息。评估QNode将自动返回梯度:

dev = qml.device("default.qubit")

@qml.gradients.param_shift
@qml.qnode(dev)
def decorated_circuit(weights):
    qml.RX(weights[0], wires=0)
    qml.RY(weights[1], wires=1)
    qml.CNOT(wires=[0, 1])
    qml.RX(weights[2], wires=1)
    return qml.probs(wires=1)
>>> decorated_circuit(weights)
tensor([[-0.04673668, -0.09442394, -0.14409127],
        [ 0.04673668,  0.09442394,  0.14409127]], requires_grad=True)

注意

如果您的电路包含任何梯度变换不支持的操作,该变换将尝试自动将电路分解为仅支持梯度的操作。

注意

如果您希望仅返回纯粹的 量子 组件的梯度——即,输出相对于 参数的梯度,而不是 QNode 参数的梯度——在应用变换时传递 hybrid=False:

>>> qml.gradients.param_shift(circuit, hybrid=False)(weights)
(tensor([-0.04673668,  0.04673668], requires_grad=True),
 tensor([-0.09442394,  0.09442394], requires_grad=True),
 tensor([-0.14409127,  0.14409127], requires_grad=True))

区分梯度变换和高阶导数

梯度变换本身是可微的,允许计算高阶梯度:

dev = qml.device("default.qubit")

@qml.qnode(dev)
def circuit(weights):
    qml.RX(weights[0], wires=0)
    qml.RY(weights[1], wires=1)
    qml.CNOT(wires=[0, 1])
    qml.RX(weights[2], wires=1)
    return qml.expval(qml.Z(1))
>>> weights = np.array([0.1, 0.2, 0.3], requires_grad=True)
>>> circuit(weights)
tensor(0.9316158, requires_grad=True)
>>> qml.gradients.param_shift(circuit)(weights)  # gradient
tensor([-0.09347337, -0.18884787, -0.28818254], requires_grad=True)
>>> def stacked_output(weights):
...     return qml.numpy.stack(qml.gradients.param_shift(circuit)(weights))
>>> qml.jacobian(stacked_output)(weights)  # hessian
array([[-0.9316158 ,  0.01894799,  0.0289147 ],
       [ 0.01894799, -0.9316158 ,  0.05841749],
       [ 0.0289147 ,  0.05841749, -0.9316158 ]])

计算高阶导数的另一种方法是将 max_diffdiff_method 参数传递给 QNode,并通过连续求导来实现:

@qml.qnode(dev, diff_method="parameter-shift", max_diff=2)
def circuit(weights):
    qml.RX(weights[0], wires=0)
    qml.RY(weights[1], wires=1)
    qml.CNOT(wires=[0, 1])
    qml.RX(weights[2], wires=1)
    return qml.expval(qml.Z(1))
>>> weights = np.array([0.1, 0.2, 0.3], requires_grad=True)
>>> qml.jacobian(qml.jacobian(circuit))(weights)  # hessian
array([[-0.9316158 ,  0.01894799,  0.0289147 ],
       [ 0.01894799, -0.9316158 ,  0.05841749],
       [ 0.0289147 ,  0.05841749, -0.9316158 ]])

请注意,max_diff 参数仅适用于梯度变换,其默认值为 1;未正确设置其值可能会导致高阶导数的结果不正确。此外,传递 diff_method="parameter-shift" 等同于传递 diff_method=qml.gradients.param_shift

转换轨道

梯度变换可以应用于低级 QuantumTape 对象,这是表示变分量子算法的数据结构:

weights = np.array([0.1, 0.2, 0.3], requires_grad=True)

ops = [
    qml.RX(weights[0], wires=0),
    qml.RY(weights[1], wires=1),
    qml.CNOT(wires=[0, 1]),
    qml.RX(weights[2], wires=1)]
measurements = [qml.expval(qml.Z(1))]
tape = qml.tape.QuantumTape(ops, measurements)

与转换 QNode 不同,直接转换 tape 不会进行隐式量子设备评估。相反,它返回处理后的 tapes 和一个后处理函数,这两个共同定义了梯度:

>>> gradient_tapes, fn = qml.gradients.param_shift(tape)
>>> gradient_tapes
[<QuantumTape: wires=[0, 1], params=3>,
 <QuantumTape: wires=[0, 1], params=3>,
 <QuantumTape: wires=[0, 1], params=3>,
 <QuantumTape: wires=[0, 1], params=3>,
 <QuantumTape: wires=[0, 1], params=3>,
 <QuantumTape: wires=[0, 1], params=3>]

如果需要分析表示梯度计算的基础电路,这可能会很有用。

然后可以评估输出磁带并进行后处理以获取梯度:

>>> dev = qml.device("default.qubit")
>>> fn(qml.execute(gradient_tapes, dev, None))
(tensor(-0.09347337, requires_grad=True),
 tensor(-0.18884787, requires_grad=True),
 tensor(-0.28818254, requires_grad=True))

请注意,梯度变换返回的后处理函数 fn 应用于从执行梯度带所得的平坦结果列表。

自定义渐变转换

使用qml.transform装饰器,可以创建自定义梯度变换:

from pennylane.tape import QuantumScriptBatch
from pennylane.typing import PostprocessingFn

@transform
def my_custom_gradient(tape: qml.tape.QuantumScript, **kwargs) -> tuple[QuantumScriptBatch, PostprocessingFn]:
    ...
    return gradient_tapes, processing_fn

一旦创建,自定义梯度变换可以直接应用于QNodes,或注册为在自动微分过程中使用的量子梯度变换。

有关更多详细信息,请参见qml.transform文档。