About quantum phase estimation code

Hi, everyone. I am confused about QPE here. I am familiar with qiskit but pennylane is a new tool for me. If I want to estimate the phase, whose eigenvalue is 7/8 (three qubits). The results are

array([[1., 0.],
       [1., 0.],
       [1., 0.],
       [1., 0.]]) 

I don’t know where goes wrong. Maybe the stupid question but I am still trying to figure it out. Hope I can get some suggestion. Thank you!

My code:

import pennylane as qml
from pennylane import numpy as np
from decimal import Decimal
from math import pi

dev2 = qml.device('default.qubit', wires = 4, analytic = True)

@qml.qnode(dev2)
def qpe():
    #QFT
    qml.Hadamard(wires=0)
    qml.CRZ(pi/2,wires=[1,0])
    qml.CRZ(pi/4, wires=[2,0])
    qml.Hadamard(wires=1)
    qml.CRZ(pi/2, wires=[2,1])
    qml.Hadamard(wires=2)
    qml.SWAP(wires=[0,2])
    
    #CU
    #qml.Hadamard(wires=0)
    #qml.Hadamard(wires=1)
    #qml.Hadamard(wires=2)

    qml.CRZ(7*pi,wires=[3,0])
    qml.CRZ(3.5*pi,wires=[3,1])
    qml.CRZ(1.75*pi,wires=[3,2])

    #inverse QFT
    qml.SWAP(wires=[0,2])
    qml.Hadamard(wires=2)
    qml.CRZ(pi/2, wires=[2,1])
    qml.Hadamard(wires=1)
    qml.CRZ(pi/4, wires=[2,0])
    qml.CRZ(pi/2,wires=[1,0])
    qml.Hadamard(wires=0)

    return qml.probs(0),qml.probs(1),qml.probs(2),qml.probs(3)

Hope someone can help me out (Also, I use the print(.draw()) and the circuit is too messy, not intuitive)

I think I need to get the result like “0b111”

Hey @3dB!

PennyLane has not focused too much on quantum phase estimation, so some of the building blocks like the quantum Fourier transform are missing (hence having to implement it yourself). Although this is something we’d like to look at in future.

I had a quick go at setting this up:

import pennylane as qml
from pennylane import numpy as np

n_register = 3
dev = qml.device('default.qubit', wires=(n_register + 1), analytic=True)

@qml.qnode(dev)
def qpe(phi):
    
    for i in range(n_register):
        qml.Hadamard(wires=i)
    
    for i in range(n_register):
        qml.CRZ(phi * (2 ** i), wires=[i, n_register])

    #inverse QFT
    qml.Hadamard(wires=2)
    qml.CRZ(-np.pi, wires=[2, 1])
    qml.Hadamard(wires=1)
    qml.CRZ(-np.pi / 2, wires=[2, 0])
    qml.CRZ(-np.pi, wires=[1, 0])
    qml.Hadamard(wires=0)

    return qml.probs(wires=[0, 1, 2, 3])

This is definitely just a quick bit of code, so don’t rely on it too much - it should be checked through.

One of the key things I spotted was that return qml.probs(0),qml.probs(1),qml.probs(2),qml.probs(3) is not right - this returns the marginal probabilities for each qubit. Instead we need the full distribution return qml.probs(wires=[0, 1, 2, 3]). Also, the QFT I used looked a bit different to yours, but they may be equivalent.

1 Like

Thank you Tom! But, I still not understand the “return” part. Does that mean I need to return the probs of Wires instead of the actual value “0” or “1”? Sorry for this simple question. And if I want to draw a histogram, how could I operate. (I can’t find any documents that teach me to draw a histogram by pennylane)

Hey @3dB,

Take my answer with a grain of salt but it might help until the devs wake up.

You’ll notice that in the instantiation of your device dev2 = qml.device('default.qubit', wires = 4, analytic = True) you have analytic = True. This implies that you are actually performing a state-vector simulation where it computes the complex-valued |\Psi> , directly. By calling qml.probs(0), qml.probs(1), you are computing the probability that the first qubit is a zero regardless of the value of the other qubits. To see why this might be a problem, consider the two states |\Psi>_1 = (|00> + |11>)/\sqrt(2) and |\Psi>_2 = (|10> + |01>)/\sqrt(2). In either case, the probabilities of a 0 for the first qubit is 50% and 0 for the second qubit is also 50%. Whereas the probability of 01 for the first state is 0 and 50% for the second. This is what Tom means when he mentions that it is the marginal probaility. Instead, qml.probs(wires=[0,1,2,3]) will return a 2^4 = 16 length vector where the index of the element is the same as the binary value of that element.

With this, you can sample from it yourself using the probability distribution. In native python, there is a solution using the random module which might look something like this. To get bitstrings you might do something like this

from random import choices
prob_dist = qpe()
values = [format(i,'04b') for i in range(len(prob_dist))]
samples = [choices(values, prob_dist) for _ in num_samples]

Which you can then produce a histogram with whatever is your favourite plotting library is. If you struggle with this part, here’s a stack-overflow post that might be of help. https://stackoverflow.com/questions/28418988/how-to-make-a-histogram-from-a-list-of-strings-in-python

Another option is to use the qml.sample() function instead of qml.probs(). This makes it feel more like quantum computer where the output is stochastic and ‘one at a time’. https://pennylane.readthedocs.io/en/stable/code/api/pennylane.sample.html

2 Likes

@milanleonard Hi,
Thank you so much for your kind explanations. I have understood! Thank you!

Thanks @milanleonard for the great answer! :100:

In terms of visualizing the results, you could also just directly plot the output distribution using matplotlib, e.g., with a line or bar chart.

Thank you. @Tom_Bromley By the way, I think your code needs to be added swap gate at the bottom of QFT?

Thanks @3dB, yes it might need some tweaks! I was following a reversed version of the diagram here.

Thank you, :smile: @Tom_Bromley

1 Like