-
Notifications
You must be signed in to change notification settings - Fork 603
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Incorrect value of gradients at 0 after taking gradient twice #1125
Comments
🤔 This is very strange, since the above code should be using the autograd/backprop pipeline. I wonder if this is a bug in Autograd? @glassnotes if you set |
I remember reading discussions on pytorch and tensorflow about these singularity issues before. I think the verdict was just that one needs to know that this can happen, because numerical stability cannot be guaranteed... |
Trying any other differentiation method gives me Here's the full traceback, if that's helpful:
|
Oh, that is because Note that TensorFlow outputs the same value, also using backprop: import pennylane as qml
import tensorflow as tf
dev = qml.device("default.qubit", wires=1)
# Produces function cos(x)
@qml.qnode(dev, interface="tf")
def circuit(x):
qml.RY(x, wires=0)
return qml.expval(qml.PauliZ(0))
x = tf.Variable(0., dtype=tf.float64)
with tf.GradientTape() as tape1:
with tf.GradientTape() as tape2:
res = circuit(x)
grad = tape2.gradient(res, x)
grad2 = tape1.gradient(grad, x)
print(grad2) # Should be -1, but outputs -0.5 |
Running the following using #1129, import pennylane as qml
import torch
dev = qml.device("default.qubit", wires=1)
# Produces function cos(x)
@qml.qnode(dev, interface="torch", diff_method="parameter-shift")
def circuit(x):
qml.RY(x, wires=0)
return qml.expval(qml.PauliZ(0))
x = torch.tensor(0., requires_grad=True)
grad2 = torch.autograd.functional.hessian(circuit, x)
print(grad2) # outputs -1 it works. I think that one of the gate operations we are using must have a singularity/be ill-conditioned for x=0 when using backpropagation. |
@josh146 noted to explore the computation of the expectation value more and managed to find an odd behaviour when using import pennylane as qml
from pennylane import numpy as np
def RY(x):
return np.array([[np.cos(x/2), -np.sin(x/2)],[np.sin(x/2), np.cos(x/2)]])
for func in [np.abs, lambda x: x]:
def circuit(x):
state = np.array([1,0])
rot = RY(*x) @ state
eigs = np.array([1, -1])
probs = func(rot) ** 2
return np.dot(eigs, probs)
gradcos2 = qml.grad(qml.grad(circuit)) # -cos(x)
print(gradcos2(np.array([0.], requires_grad=True))) # Should be -1, but outputs when using a QNode -0.5
For |
Is this something that will be fixed through #1129 then or a more global issue with numpy? This seems very strange 😕 |
@glassnotes this will be fixed in #1129 insomuch as that the following code will run with We have two potential solutions:
|
Technically the "correct" derivative of |
I looked at this a bit, and a fix would be adding the prob = self.marginal_prob(self._abs(self._flatten(self._state)) ** 2, wires) by prob = self.marginal_prob(self._flatten(self._real(self._state) ** 2 + self._imag(self._state) ** 2), wires) This does fix the issue in all frameworks. However, the performance discussion is properly complex (pun not intended): A simple circuit evaluation on 2-10 qubits seemed to show a small deprecation in performance with the proposed change above and variants thereof (e.g. first flattening, storing the result and extracting the real and imaginary part from there, leading to flat arrays being squared.) For larger systems and Autograd, the above seems to be a bit faster. |
Thanks for the deep dive on this @dwierichs!
How much of a performance degradation do you notice?
This somewhat makes me lean towards making this change, as the overhead does not appear to be wrt qubit number |
It seems to not grow significantly with the system size and is on the order of tens of milliseconds. However it should be noted, that It should be noted that this bug is an edge case, but I still think it would be good to fix it at the cost of the above small performance decrease. |
Issue description
A colleague noticed that when running
qml.grad
twice on a particular circuit, to compute a second derivative, the output value of the gradient is incorrect when the input value is precisely 0. The specific circuit is effectively the functioncos(x)
(see below for the code to reproduce).Expected behavior: The value of the second gradient at 0 for this function should be -1.
Actual behavior: The value of the second gradient at 0 is computed to be -0.5.
Reproduces how often: Always
System information: (post the output of
import pennylane as qml; qml.about()
)Source code and tracebacks
Code to reproduce:
The issue seems to be at 0 specifically, as evaluating the gradient at
1e-6
yields a result very close to -1.I tried another example as well:
The text was updated successfully, but these errors were encountered: