Use this topic to ask your questions about the PennyLane Challenge: Universality of single-qubit gates.
I have implemented the error function as
def error(U, params):
matrix = get_matrix(params)
z = np.ravel(np.square(np.abs(U - matrix)))
z = np.sum(qml.math.toarray(z))
return z.item()
which is \displaystyle\sum_i\sum_j|U_{ij}-M_{ij}|^2.
With the given seed, the optimization passes the [[1, 0], [0, -1]]
case rather easily, but fo test case [[0.70710678, 0.70710678], [0.70710678, -0.70710678]]
with this error function the optimization does not converge fast enough.
I have also tried with taking max instead of sum but still, even though they are good enough to pass the [[1, 0], [0, -1]]
case the other case still does not converge.
matrix = get_matrix(list(params))
z = qml.math.toarray(np.max(np.abs((np.ravel(U - matrix)))))
return z.item()
Any hints would appreciated.
Hi @LdBeth ,
It’s much simpler than that. You don’t need to use np.ravel or many of the other things you’re using. You want U and matrix to be the same. What’s the simplest way that you can calculate a single number that tells you how different these two matrices are? The solution isn’t fancy but it works!
I hope this helps.
I have to hand roll these because the most obvious to me np.linalg.norm
does not work here,
Runtime Error: Failed to execute run() function: loop of ufunc does not support argument 0 of type ArrayBox which has no callable sqrt method
If norm doesn’t work, I guess I should go for trace the of differences?
I think I know it is using the autograd package to compute the derivative but unfortunately I have no clue on where I can learn what functions are supported and apparently not all numpy function are included, for example sqrt.
Thanks, and it turns out the fault is not cause by my error function, in fact \sum_i\sum_j|U_{ij}-M_{ij}|^2 is the correct way to go and my first error function is almost correct. (and it is certainly fine to use np.ravel
.
The issue is with my get_matrix
implementation, for some strange beliefs I wrote np.matmul(rz2, rx, rz1)
, although give a (2, 2)
shaped array, which turns out is not the same as (rz2 @ rx @ rz1)
, after fixed this all tests are passed.
As a suggestion, maybe it would be better if additional checks to the get_matrix
result is added
I’m glad you solved it @LdBeth ! And thanks for the suggestion, I will share it with our team.
Oh, I forget to check the private test, which failed without revealing the reason why it has failed, and I now I have really no clues. My implementation is attached below.
def get_matrix(params):
alpha, beta, gamma, phi = params
a, b = np.cos(beta / 2), -1j * np.sin(beta / 2)
rx = np.matrix([[a, b], [b, a]])
rz1 = np.matrix([[np.exp(-1j * alpha / 2), 0], [0, np.exp(1j * alpha / 2)]])
rz2 = np.matrix([[np.exp(-1j * gamma / 2), 0], [0, np.exp(1j * gamma / 2)]])
return np.exp(1j * phi) * (rz2 @ rx @ rz1)
def error(U, params):
# Put your code here #
# Return the error
return sum(sum(qml.math.toarray(np.square(np.abs(U-matrix)))))
Hi @LdBeth ,
For the get_matrix function you can use qml.RZ() and qml.RX() to create a unitary. Then you can use qml.matrix() to turn that unitary into a matrix.
For the error you don’t need to square it or turn it into an array.
I see, thanks! So that is all because the matrices I manually computed has caused much worse autograd performance, which gives more than 0.01 difference in the optimized result and failed to converge for other tests.
That could be it @LdBeth !
Hello,
My code outputs the wrong answer and I still pass the test ? Can someone help me understand why ?
Here is my get_matrix
alpha, beta, gamma, phi = params
# Put your code here #
RX_matrix = qml.matrix(qml.RX(beta, 0))
RZ_matrix_gamma = qml.matrix(qml.RZ(gamma, 0))
RZ_matrix_alpha = qml.matrix(qml.RZ(alpha, 0))
U = np.exp(1j*phi)*RZ_matrix_gamma@RX_matrix@RZ_matrix_alpha
# Return the matrix
return U
and my err
err = np.sum(np.abs(U-matrix))
return err
This is my output
Test 1 of 2 passed! ✅
Input: [[ 1, 0], [ 0, -1]]
Expected output: [[ 1, 0], [ 0, -1]]
Solution output: [1.6892801452045216, -0.0015257310053992737, 1.4371410130382238, 1.5707963267948966]
Failure message: null
Test 2 of 2 passed! ✅
Input: [[ 0.70710678, 0.70710678], [ 0.70710678, -0.70710678]]
Expected output: [[ 0.70710678, 0.70710678], [ 0.70710678, -0.70710678]]
Solution output: [1.570570205712601, 1.5707931060347167, 1.5648959446953452, 1.5707944021778046]
Failure message: null
Hi @yoshypdf ,
Thank you for pointing this out!
I think what’s happening is that the Solution output
is showing the params while the Expected output
is showing the matrix.
So even though your solution output and expected output don’t match, the solution seems indeed to be correct.
I’m not sure if/how we can fix it but at least it’s good to be aware of this issue. Thanks for making this post!
Hello,
With the previous replies in mind, it seems that the get_matrix function can be implemented with qml.matrix() and that the Frobenius norm is the applicable matrix norm for the error function. However, my implementation for get_matrix:
return np.exp(1j *phi) * (qml.matrix(qml.RZ(gamma,0)) @ qml.matrix(qml.RX(beta,0)) @ qml.matrix(qml.RZ(alpha,0)))
along with my implementation of the error function:
return np.linalg.norm(U - matrix, ord="fro")
does not pass any of the public and private tests and I’m stumped.
It’s not as simple as just returning
return np.array([[alpha, beta], [gamma, phi]])
from get_matrix, is it?
Hi!
no, your get_matrix
function is actually correct. The problem is in the error function.
I am not sure about the reason, but the measure that you are using np.linalg.norm
does not allow the model to converge to the correct answer. I can provide a better reason for this next week. But for now, I can give you a hint about the one we used. We used a simpler version just by trying to calculate the sum of all elements of the subtraction of the two matrices, somewhat similar to what you have.
Let me know if this helps.
Huh, interesting. The hint that you had provided was indeed helpful, I caught on that it ought to be the sum of the matrix elements of absolute difference between the matrices. I’d still like to know why np.linalg.norm prohibits the model’s convergence whenever you figure it out.
yeah, I am looking into it. But we believe it is a bug of qml.grad()
because it is treating return np.linalg.norm(U - matrix, ord="fro")
and return np.linalg.norm(np.abs(U - matrix), ord="fro")
(this one converges) differently when theoretically they are the same. Thanks for helping us uncovering this issue. I’ll report it to the technical team so it can be resolved or clarified further.