Closed cvjjm closed 6 months ago
Thanks @cvjjm! We're just finishing the last stages of our 0.35 release but will get back to this very soon.
It does look like there is source code notes for the two_qubit_decomposition
:
# I was not able to get this transform to be fully differentiable for unitary
# matrices that were constructed within a QNode, based on the QNode's input
# arguments. I would argue this is a fairly limited use case, but it would still
# be nice to have this eventually. Each interface fails for different reasons.
#
# - In Autograd, we obtain the AttributeError
# 'ArrayBox' object has no attribute 'conjugate'
# for the 0-CNOT case when the zyz_decomposition function is called. In the
# other cases, it cannot autodifferentiate through the linalg.eigvals function.
So this comes back to the repeating problem that we can't differentiate through eigendecomposition.
Thanks for reporting this @cvjjm!
Finally, computing gradients also does not work. Differentiating at np.pi/2 yields a NotImplementedError in autograd (autograd==1.6.2). Differentiating at random parameters yields nan, even with diff_method="finite-diff".
The comment by @albi3ro above looks to be the cause --- @trbromley @albi3ro we should likely update the QubitUnitary
docstring to include the decomposition gradient information if not already present.
- For the given in_mat input matrix the matrix returned by qml.matrix() on a QNode that contains a single QubitUnitary operation is wrong and it is off by more than just a global phase. (For other input matrices such as, i.e., in_mat = make_unitary(np.pi/2 + 1e-6) the matrices match)
- Also executing the QNode on default.qubit produces the wrong state, even though the operations on the tape look correct and when drawing the tape all looks good.
Interesting 🤔 Definitely something we need to look into.
Differentiation is not super high priority but getting the correct matrix under decomposition would be good :-)
But out of curiosity: Why does even finite-diff
differentiation not work?
This is not the root cause of the issues described above, but it is in related code and may be worth improving as well:
The perturbation with epsilon seems to be not needed because:
>>> math.arctan2(0.1, 1e-64)
1.5707963267948966
>>> math.arctan2(0.1, 0)
1.5707963267948966
Hi again! I understand that there are a few more complex issues here that need more work and are maybe not that important, but it would be great if at least running circuits with QubitUnitary
gates on default qubit would produce the correct states/expectation values.
Hey @cvjjm! This has been on our radar, but thanks to @albi3ro we have a fix in the works 😄.
Does this only fix the (minor) problem of the discontinuity or do in_mat and out_mat in the above example now also match (at least up to a global phase)?
Hi @cvjjm! Sorry for taking a while to get back to you. Running the above code with PL v0.36
coming out on Tuesday next week gives:
in_mat
[[ 1. +0.j 0. +0.j 0. +0.j 0. +0.j ]
[ 0. +0.j 0.707-0.j 0.14 -0.693j 0. +0.j ]
[ 0. +0.j -0.14 -0.693j 0.707+0.j 0. +0.j ]
[ 0. +0.j 0. +0.j 0. +0.j 1. +0.j ]]
out_mat
[[ 1. +0.j 0. -0.j -0. +0.j 0. -0.j ]
[ 0. -0.j 0.707-0.j 0.14 -0.693j -0. +0.j ]
[-0. +0.j -0.14 -0.693j 0.707+0.j 0. -0.j ]
[ 0. -0.j 0. +0.j -0. -0.j 1. -0.j ]]
global_phase 9.020562075079397e-16
out_mat with phase corrected
[[ 1. +0.j 0. -0.j -0. +0.j 0. -0.j ]
[ 0. -0.j 0.707-0.j 0.14 -0.693j -0. +0.j ]
[-0. +0.j -0.14 -0.693j 0.707-0.j 0. -0.j ]
[ 0. -0.j 0. +0.j -0. -0.j 1. -0.j ]]
difference should be zero but it is not:
[[ 0.+0.j -0.+0.j 0.-0.j -0.+0.j]
[-0.+0.j -0.+0.j 0.+0.j 0.-0.j]
[ 0.-0.j 0.+0.j -0.+0.j -0.+0.j]
[-0.+0.j -0.-0.j 0.+0.j 0.+0.j]]
circuit:
0: ─╭U(M0)─┤ ╭State
1: ─╰U(M0)─┤ ╰State
M0 =
[[ 1. +0.00000000e+00j 0. +0.00000000e+00j
0. +0.00000000e+00j 0. +0.00000000e+00j]
[ 0. +0.00000000e+00j 0.70710678-2.29934717e-17j
0.14048043-6.93011723e-01j 0. +0.00000000e+00j]
[ 0. +0.00000000e+00j -0.14048043-6.93011723e-01j
0.70710678+0.00000000e+00j 0. +0.00000000e+00j]
[ 0. +0.00000000e+00j 0. +0.00000000e+00j
0. +0.00000000e+00j 1. +0.00000000e+00j]]
tape looks correct:
[QubitUnitary(tensor([[ 1. +0.00000000e+00j, 0. +0.00000000e+00j,
0. +0.00000000e+00j, 0. +0.00000000e+00j],
[ 0. +0.00000000e+00j, 0.70710678-2.29934717e-17j,
0.14048043-6.93011723e-01j, 0. +0.00000000e+00j],
[ 0. +0.00000000e+00j, -0.14048043-6.93011723e-01j,
0.70710678+0.00000000e+00j, 0. +0.00000000e+00j],
[ 0. +0.00000000e+00j, 0. +0.00000000e+00j,
0. +0.00000000e+00j, 1. +0.00000000e+00j]], requires_grad=True), wires=[0, 1])]
state returned by qnode should be |0> but it is not:
[ 1.00000000e+00+8.32667268e-16j 1.10929801e-16-1.10606113e-15j
-7.85557000e-17+4.68148273e-16j 1.38777878e-17-1.38777878e-17j]
jac2 [nan]
This looks to be mostly working :rocket: However, we unfortunately are still getting nan
for the Jacobian. I think the problem remains because of our inability to differentiate through the eigendecomposition as @albi3ro mentioned. Also, this is still a problem in finite-diff
mode because that mode only applies finite difference to the quantum parts of the computation, the classical parts like eigendecomposition are still performed using automatic differentiation.
Expected behavior
QubitUnitary should be correctly executed for all unitary input matrices, should be (at least) finite difference differentiable, and parameter-shift differentiable via decomposition.
Actual behavior
The minimal non working example code reveals several problems:
in_mat
input matrix the matrix returned byqml.matrix()
on a QNode that contains a singleQubitUnitary
operation is wrong and it is off by more than just a global phase. (For other input matrices such as, i.e.,in_mat = make_unitary(np.pi/2 + 1e-6)
the matrices match)default.qubit
produces the wrong state, even though the operations on the tape look correct and when drawing the tape all looks good.np.pi/2
yields aNotImplementedError
inautograd
(autograd==1.6.2). Differentiating at random parameters yieldsnan
, even withdiff_method="finite-diff"
.I provide the output of the minimal non-working code and its output below.
Additional information
No response
Source code
Tracebacks
System information
Existing GitHub issues