不支持的梯度配置¶
设备雅可比¶
为了在 QNode 中使用 diff_method="device",传入 QNode 构造函数的设备必须将其 "provides_jacobian" 能力设置为 True,并且必须包含一个返回每个量子电路梯度的方法 jacobian(circuits, **kwargs)。这在 default.qubit 设备中未实现,因为该设备不提供这样的 jacobian 方法(而是允许反向传播工作)。
请参阅自定义插件页面以获取更多详细信息。
如果使用此配置,将会引发一个异常:
def print_grad():
dev = qml.device('default.qubit', wires=1, shots=None)
@qml.qnode(dev, diff_method='device')
def circuit(x):
qml.RX(x[0], wires=0)
return qml.expval(qml.Z(wires=0))
x = np.array([0.1], requires_grad=True)
print(qml.grad(circuit)(x))
>>> print_grad()
Traceback (most recent call last):
...
File "C:\pennylane\pennylane\qnode.py", line 448, in _validate_device_method
raise qml.QuantumFunctionError(
pennylane.QuantumFunctionError: The default.qubit device does not provide a native method for computing the jacobian.
反向传播¶
反向传播算法本质上是解析的,因此当使用 diff_method="backprop" 时,传递 shots=None 是唯一支持的配置。尽管即使在 shots>0 的情况下(如伴随微分的情况,见下一节),始终使用解析梯度是可能的,但在当前代码的状态下,这会破坏其他内容。
目前如果使用此无效配置,将会引发异常:
def print_grad():
dev = qml.device('default.qubit', wires=1, shots=100)
@qml.qnode(dev, diff_method='backprop')
def circuit(x):
qml.RX(x[0], wires=0)
return qml.expval(qml.Z(wires=0))
x = np.array([0.1], requires_grad=True)
print(qml.grad(circuit)(x))
>>> print_grad()
Traceback (most recent call last):
...
File "C:\pennylane\pennylane\qnode.py", line 375, in _validate_backprop_method
raise qml.QuantumFunctionError("Backpropagation is only supported when shots=None.")
pennylane.QuantumFunctionError: Backpropagation is only supported when shots=None.
更改为 shots=None 允许计算解析梯度:
def print_grad():
dev = qml.device('default.qubit', wires=1, shots=None)
@qml.qnode(dev, diff_method='backprop')
def circuit(x):
qml.RX(x[0], wires=0)
return qml.expval(qml.Z(wires=0))
x = np.array([0.1], requires_grad=True)
print(qml.grad(circuit)(x))
>>> print_grad()
[-0.09983342]
伴随微分¶
PennyLane 实现了来自 2009.02823 的伴随微分方法,该方法仅讨论可观察量期望值的梯度。
特别地,以下代码按预期工作:
def print_grad():
dev = qml.device('default.qubit', wires=1, shots=None)
@qml.qnode(dev, diff_method='adjoint')
def circuit(x):
qml.RX(x[0], wires=0)
return qml.expval(qml.Z(wires=0))
x = np.array([0.1], requires_grad=True)
print(qml.grad(circuit)(x))
>>> print_grad()
[-0.09983342]
default.qubit 可以区分任何其他测量过程,只要它在 Z 测量基中。在这种情况下,我们建议使用设备提供的 vjp (device_vjp=True) 以改善性能扩展。当最终成本函数只有一个标量值时,这个算法效果最佳。
lightning.qubit 只支持期望值。
@qml.qnode(qml.device('default.qubit'), diff_method="adjoint", device_vjp=True)
def circuit(x):
qml.IsingXX(x, wires=(0,1))
return qml.probs(wires=(0,1))
def cost(x):
probs = circuit(x)
target = np.array([0, 0, 0, 1])
return qml.math.norm(probs-target)
>>> qml.grad(cost)(qml.numpy.array(0.1))
-0.07059288589999416
此外,伴随微分算法本质上是解析的。如果执行中有 shots>0,则会引发错误:
def print_grad_ok():
dev = qml.device('default.qubit', wires=1, shots=100)
@qml.qnode(dev, diff_method='adjoint')
def circuit(x):
qml.RX(x[0], wires=0)
return qml.expval(qml.Z(wires=0))
x = np.array([0.1], requires_grad=True)
print(qml.grad(circuit)(x))
>>> print_grad_ok()
DeviceError: Finite shots are not supported with adjoint + default.qubit
状态梯度¶
一般来说,量子电路的状态将是复数值的,因此在不使用 复分析的情况下直接对状态进行微分是不可能的。尽管大多数“简单”函数可以实现复数梯度,但在Autograd中不支持,而是在其他三个接口中完成。
相反,在Autograd中,应对输出状态进行实数标量值后处理,以允许自动微分框架进行反向传播。例如,以下代码使用一个依赖于输出状态的标量成本函数:
def state_scalar_grad():
dev = qml.device('default.qubit', wires=1, shots=None)
@qml.qnode(dev, diff_method='backprop')
def circuit(x):
qml.RX(x[0], wires=0)
return qml.state()
def cost_fn(x):
out = circuit(x)
return np.abs(out[0])
x = np.array([0.1], requires_grad=True)
print(qml.grad(cost_fn)(x))
>>> state_scalar_grad()
[-0.02498958]
然而,从对标量成本进行微分转变为直接对状态进行微分将会导致错误:
def state_vector_grad():
dev = qml.device('default.qubit', wires=1, shots=None)
@qml.qnode(dev, diff_method='backprop')
def circuit(x):
qml.RX(x[0], wires=0)
return qml.state()
x = np.array([0.1], requires_grad=True)
print(qml.jacobian(circuit)(x))
>>> state_vector_grad()
Traceback (most recent call last):
...
File "C:\Python38\lib\site-packages\numpy\core\fromnumeric.py", line 57, in _wrapfunc
return bound(*args, **kwds)
ValueError: cannot reshape array of size 4 into shape (2,1)
使用支持复合微分的不同接口将修复此错误:
def state_vector_grad_jax():
dev = qml.device('default.qubit', wires=1, shots=None)
@qml.qnode(dev, interface='jax', diff_method='backprop')
def circuit(x):
qml.RX(x[0], wires=0)
return qml.state()
x = jnp.array([0.1], dtype=np.complex64)
print(jax.jacrev(circuit, holomorphic=True)(x))
def state_vector_grad_tf():
dev = qml.device('default.qubit', wires=1, shots=None)
@qml.qnode(dev, interface='tf', diff_method='backprop')
def circuit(x):
qml.RX(x[0], wires=0)
return qml.state()
x = tf.Variable([0.1], trainable=True, dtype=np.complex64)
with tf.GradientTape() as tape:
out = circuit(x)
print(tape.jacobian(out, [x]))
def state_vector_grad_torch():
dev = qml.device('default.qubit', wires=1, shots=None)
@qml.qnode(dev, interface='torch', diff_method='backprop')
def circuit(x):
qml.RX(x[0], wires=0)
return qml.state()
x = torch.tensor([0.1], requires_grad=True, dtype=torch.complex64)
print(torch.autograd.functional.jacobian(circuit, (x,)))
>>> state_vector_grad_jax()
[[-0.02498958+0.j ]
[ 0. -0.49937513j]]
>>> state_vector_grad_tf()
[<tf.Tensor: shape=(2, 1), dtype=complex64, numpy=
array([[-0.02498958+0.j ],
[-0. +0.49937513j]], dtype=complex64)>]
>>> state_vector_grad_torch()
(tensor([[-0.0250+0.0000j],
[ 0.0000+0.4994j]]),)
示例梯度¶
在Pennylane中,样本是从可观测量的特征值中抽取的,如果没有提供可观测量,则从计算基态中抽取。这个过程通常是不可微分的,因此不允许通过采样进行梯度反向传播。
目前,在这种情况下尝试计算梯度不会引发错误,但结果将是不正确的:
def sample_backward():
dev = qml.device('default.qubit', wires=1, shots=20)
@qml.qnode(dev)
def circuit(x):
qml.RX(x[0], wires=0)
return qml.sample(wires=0)
x = np.array([np.pi / 2])
print(qml.jacobian(circuit)(x))
>>> sample_backward()
[[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]
[0.5]]
前向传播得到支持,并将按预期运行:
def sample_forward():
dev = qml.device('default.qubit', wires=1, shots=20)
@qml.qnode(dev)
def circuit(x):
qml.RX(x[0], wires=0)
return qml.sample(wires=0)
x = np.array([np.pi / 2])
print(circuit(x))
>>> sample_forward()
[0 1 0 0 0 1 1 0 0 1 1 1 0 0 0 1 1 0 0 0]