Quantum Fourier Transform

Hey I am new to PennyLanne and Quantum Computing. I am trying to implement quantum Fourier transform on using PennyLane. The following code was adapted from Qiskit at: https://qiskit.org/textbook/ch-algorithms/quantum-fourier-transform.html

My goal is the replicate the code for QFT in this link using PennyLane. I got error when I tried, I am not sure what I am missing. Below is the code:

import pennylane as qml
from pennylane import numpy as np

n_register = 3
dev = qml.device(‘default.qubit’, wires=(n_register), analytic=True)

@qml.qnode(dev)

def qft_rotations(qml, n):
“”“Performs qft on the first n qubits in circuit (without swaps)”""
n = int(n)
if n == 0:
return qml
n -= 1
qml.Hadamard(wires=n)
for qubit in range(n):
qml.CRZ(pi/2**(n-qubit), wires=[qubit, n])
qft_rotations(n)

def swap_registers(qml, n):
for qubit in range(n//2):
qml.SWAP(wires = [qubit, n-qubit-1])
return qml

def qft(qml, n):
“”“QFT on the first n qubits in circuit”""
qft_rotations(qml, n)
swap_registers(qml, n)
return qml

result = qft(qml,3)
print(result.draw())

Hi @gidu — Welcome to the forum.

Before being able to help, could I ask you to

  • Adjust the formatting of the code (e.g., enclosing strings with "" and '' instead of ““ , double-checking that imports are correct and the code snippet can be executed independently)
  • Place code into a highlighted code blocks so that the code renders and indentation is correct

Overall this helps with quickly recreating the case that is shared and also helps other users easily get up to speed with parts of the solution.

Thanks!

Thanks you very much @Nicolas_Quesada, here is the edited version:

Hey I am new to PennyLanne and Quantum Computing. I am trying to implement quantum Fourier transform on using PennyLane. The following code was adapted from Qiskit at: https://qiskit.org/textbook/ch-algorithms/quantum-fourier-transform.html

My goal is the replicate the code for QFT in this link using PennyLane. I got error when I tried, I am not sure what I am missing. Below is the code:

import pennylane as qml
from pennylane import numpy as np

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


@qml.qnode(dev)

def qft_rotations(qml, n):
    """Performs qft on the first n qubits in circuit (without swaps)"""
    n = int(n)
    if n == 0:
        return qml
    n -= 1
    qml.Hadamard(wires=n)
    for qubit in range(n):
        qml.CRZ(pi/2**(n-qubit), wires=[qubit, n])
    qft_rotations(n)

def swap_registers(qml, n):
    for qubit in range(n//2):
        qml.SWAP(wires = [qubit, n-qubit-1])
    return qml

def qft(qml, n):
    """QFT on the first n qubits in circuit"""
    qft_rotations(qml, n)
    swap_registers(qml, n)
    return qml

result = qft(qml,3)
print(result.draw())

Thanks @gidu. Could you also tell us which error are you getting?

From looking at you code, one problem is that you are importing pennylane as qml but you are also using this name for the input variable of one of your functions.

Hello @Nicolas_Quesada, even without qml in the input argument, when I run the function: qft_rotations(3) :

import pennylane as qml
from pennylane import numpy as np

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


@qml.qnode(dev)

def qft_rotations(n):
    """Performs qft on the first n qubits in circuit (without swaps)"""
    n = int(n)
    if n == 0:
        return qml
    n -= 1
    qml.Hadamard(wires=n)
    for qubit in range(n):
        qml.CRZ(pi/2**(n-qubit), wires=[qubit, n])
    qft_rotations(n)

I got the following error:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-f25d837ec086> in <module>()
----> 1 qft_rotations(3)

~\Anaconda3\lib\site-packages\pennylane\interfaces\autograd.py in __call__(self, *args, **kwargs)
     67             self.set_trainable(args)
     68             args = autograd.builtins.tuple(args)  # pylint: disable=no-member
---> 69             return self.evaluate(args, kwargs)
     70 
     71         @staticmethod

~\Anaconda3\lib\site-packages\autograd\tracer.py in f_wrapped(*args, **kwargs)
     46             return new_box(ans, trace, node)
     47         else:
---> 48             return f_raw(*args, **kwargs)
     49     f_wrapped.fun = f_raw
     50     f_wrapped._is_autograd_primitive = True

~\Anaconda3\lib\site-packages\pennylane\qnodes\base.py in evaluate(self, args, kwargs)
    819 
    820         if self.circuit is None or self.mutable:
--> 821             self._construct(args, kwargs)
    822 
    823         self.device.reset()

~\Anaconda3\lib\site-packages\pennylane\qnodes\jacobian.py in _construct(self, args, kwargs)
     88         for each positional parameter.
     89         """
---> 90         super()._construct(args, kwargs)
     91         self.par_to_grad_method = {k: self._best_method(k) for k in self.variable_deps}
     92 

~\Anaconda3\lib\site-packages\pennylane\qnodes\base.py in _construct(self, args, kwargs)
    564                     # it's ok to directly pass auxiliary arguments since the circuit is re-constructed each time
    565                     # (positional args must be replaced because parameter-shift differentiation requires Variables)
--> 566                     res = self.func(*self.arg_vars, **kwargs)
    567                 else:
    568                     # TODO: Maybe we should only convert the kwarg_vars that were actually given

<ipython-input-2-fe55010aff02> in qft_rotations(n)
     10 def qft_rotations(n):
     11     """Performs qft on the first n qubits in circuit (without swaps)"""
---> 12     n = int(n)
     13     if n == 0:
     14         return qml

TypeError: int() argument must be a string, a bytes-like object or a number, not 'Variable'

Running the following code without qml in the argument:

import pennylane as qml
from pennylane import numpy as np

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


@qml.qnode(dev)

def qft_rotations(n):
    """Performs qft on the first n qubits in circuit (without swaps)"""
    n = int(n)
    if n == 0:
        return qml
    n -= 1
    qml.Hadamard(wires=n)
    for qubit in range(n):
        qml.CRZ(pi/2**(n-qubit), wires=[qubit, n])
    qft_rotations(n)

def swap_registers(n):
    for qubit in range(n//2):
        qml.SWAP(wires = [qubit, n-qubit-1])
    return qml

def qft(n):
    """QFT on the first n qubits in circuit"""
    qft_rotations(qml, n)
    swap_registers(qml, n)
    return qml

result = qft(3)
print(result.draw())

gives this error:

TypeError                                 Traceback (most recent call last)
<ipython-input-2-fe55010aff02> in <module>()
     30     return qml
     31 
---> 32 result = qft(3)
     33 print(result.draw())

<ipython-input-2-fe55010aff02> in qft(n)
     26 def qft(n):
     27     """QFT on the first n qubits in circuit"""
---> 28     qft_rotations(qml, n)
     29     swap_registers(qml, n)
     30     return qml

~\Anaconda3\lib\site-packages\pennylane\interfaces\autograd.py in __call__(self, *args, **kwargs)
     67             self.set_trainable(args)
     68             args = autograd.builtins.tuple(args)  # pylint: disable=no-member
---> 69             return self.evaluate(args, kwargs)
     70 
     71         @staticmethod

~\Anaconda3\lib\site-packages\autograd\tracer.py in f_wrapped(*args, **kwargs)
     46             return new_box(ans, trace, node)
     47         else:
---> 48             return f_raw(*args, **kwargs)
     49     f_wrapped.fun = f_raw
     50     f_wrapped._is_autograd_primitive = True

~\Anaconda3\lib\site-packages\pennylane\qnodes\base.py in evaluate(self, args, kwargs)
    819 
    820         if self.circuit is None or self.mutable:
--> 821             self._construct(args, kwargs)
    822 
    823         self.device.reset()

~\Anaconda3\lib\site-packages\pennylane\qnodes\jacobian.py in _construct(self, args, kwargs)
     88         for each positional parameter.
     89         """
---> 90         super()._construct(args, kwargs)
     91         self.par_to_grad_method = {k: self._best_method(k) for k in self.variable_deps}
     92 

~\Anaconda3\lib\site-packages\pennylane\qnodes\base.py in _construct(self, args, kwargs)
    550         # pylint: disable=attribute-defined-outside-init, too-many-branches, too-many-statements
    551 
--> 552         self.arg_vars, self.kwarg_vars = self._make_variables(args, kwargs)
    553 
    554         # temporary queues for operations and observables

~\Anaconda3\lib\site-packages\pennylane\qnodes\base.py in _make_variables(self, args, kwargs)
    482         arg_vars = [
    483             i.item() if isinstance(i, np.ndarray) and i.ndim == 0 else i
--> 484             for i in unflatten(arg_vars, args)
    485         ]
    486 

~\Anaconda3\lib\site-packages\pennylane\utils.py in unflatten(flat, model)
    176     """
    177     # pylint:disable=len-as-condition
--> 178     res, tail = _unflatten(np.asarray(flat), model)
    179     if len(tail) != 0:
    180         raise ValueError("Flattened iterable has more elements than the model.")

~\Anaconda3\lib\site-packages\pennylane\utils.py in _unflatten(flat, model)
    158         res = []
    159         for x in model:
--> 160             val, flat = _unflatten(flat, x)
    161             res.append(val)
    162         return res, flat

~\Anaconda3\lib\site-packages\pennylane\utils.py in _unflatten(flat, model)
    162         return res, flat
    163 
--> 164     raise TypeError("Unsupported type in the model: {}".format(type(model)))
    165 
    166 

TypeError: Unsupported type in the model: <class 'module'>

Hi @gidu — I think you misunderstood my suggestion. It is not that you should remove your first argument is that you are using the word qml to mean two different things.

Hi @Nicolas_Quesada, I am trying to return the circuit (qml) if a given condition is met, I changed the code to return the circuit (dev) as follows (the equivalent implementation in qiskit is below):

def qft_rotations(n):
    """Performs qft on the first n qubits in circuit (without swaps)"""
    n = int(n)
    if n == 0:
        return dev
    n -= 1
    qml.Hadamard(wires=n)
    for qubit in range(n):
        qml.CRZ(np.pi/2**(n-qubit), wires=[qubit, n])
    qft_rotations(n)

result = qft_rotations(3)
print(result.draw())

Error Obtained:

AttributeError                            Traceback (most recent call last)
<ipython-input-17-48eea1b84893> in <module>()
     11 
     12 result = qft_rotations(3)
---> 13 print(result.draw())

AttributeError: 'NoneType' object has no attribute 'draw'

Qiskit Implementation:

def qft_rotations(circuit, n):
    if n == 0: # Exit function if circuit is empty
        return circuit
    n -= 1 # Indexes start from 0
    circuit.h(n) # Apply the H-gate to the most significant qubit
    for qubit in range(n):
        # For each less significant qubit, we need to do a
        # smaller-angled controlled rotation: 
        circuit.cu1(pi/2**(n-qubit), qubit, n)

Hi @gidu — Could you post a complete code snippet that produces the error you are getting?

Hi @Nicolas_Quesada, here is the code:

import pennylane as qml
from pennylane import numpy as np

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

def qft_rotations(n):
    """Performs qft on the first n qubits in circuit (without swaps)"""
    n = int(n)
    if n == 0:
        return dev
    n -= 1
    qml.Hadamard(wires=n)
    for qubit in range(n):
        qml.CRZ(np.pi/2**(n-qubit), wires=[qubit, n])
    qft_rotations(n)

result = qft_rotations(n_register)
print(result.draw())

Hi @gidu — As written you function qft_rotations is returning None, which is why you are getting an error. Seems like this function is missing a return statement.

1 Like

How can I make the function return the circuit when n get to zero (n=0)? And I would like to see how the circuit is drawn. Thanks.

Hi @gidu,

Sure thing! There are two related characteristics to keep in mind when creating a QNode in PennyLane:

  1. Every QNode needs to have a return statement specifying the type of the statistics to use (e.g., qml.expval for obtaining the expectation value, qml.samples to obtain samples by running the circuit, etc.) and the observable (e.g., qml.PauliZ(0) for PauliZ to act on the first qubit)
  2. Arguments to the circuit that are passed as positional arguments will be regarded as differentiable parameters. Non-differntiable parameters should be passed as keyword arguments to the quantum function (and should have a default value in the signature)

For the sake of the example, added qml.expval(qml.PauliZ(0)) to satisfy the 1. point and made n a keyword argument (specified a default value) to satisfy the 2. point.

Further note that the return statement for qft_rotations was made “empty” (returning None implicitly), as calls to qft_rotations will build up the circuit quantum function which has return qml.expval(qml.PauliZ(0)) as the return.

The snippet looks as follows with this modifications:

import pennylane as qml
from pennylane import numpy as np

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

def qft_rotations(n=0):
    """Performs qft on the first n qubits in circuit (without swaps)"""
    n = int(n)
    if n == 0:
        return
    n -= 1
    qml.Hadamard(wires=n)
    for qubit in range(n):
        qml.CRZ(np.pi/2**(n-qubit), wires=[qubit, n])
    qft_rotations(n=n)


def circuit(n=0):
    qft_rotations(n=n)
    return qml.expval(qml.PauliZ(0))

circuit = qml.QNode(circuit, dev)
circuit(n=3)
print(circuit.draw())

One way to draw the circuit is to specify the QNode explicitly, evaluate the quantum function (circuit(n=3)) and print the drawn circuit (print(circuit.draw())).

Let us know if you have further questions!

Thank you very much @antalszava. Your explanation and code give me some great insights. I adapted your code to implement QFT for n-qubits, but I am not sure if this is correct. I would appreciate your feedback. Also, please how can I extract the probabilities for the different bases?

import pennylane as qml
from pennylane import numpy as np

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

def qft_rotations(n=0):
    """Performs qft on the first n qubits in circuit (without swaps)"""
    n = int(n)
    if n == 0:
        return
    n -= 1
    qml.Hadamard(wires=n)
    for qubit in range(n):
        qml.CRZ(np.pi/2**(n-qubit), wires=[qubit, n])
    qft_rotations(n=n)

def swap_registers(n):
    for qubit in range(n//2):
        qml.SWAP(wires = [qubit, n-qubit-1])
    #return

def qft(n=0):
    """QFT on the first n qubits in circuit"""
    qft_rotations(n=n)
    swap_registers(n=n)
    return qml.probs(wires=[0, 1, 2])

circuit = qml.QNode(qft, dev)
circuit(n=3)
print(circuit.draw())

qft(n=3)

When I run the above QFT function for n = 3 I get the following: probs(wires=[0, 1, 2]) as my output. I would like to get the actual probabilities displayed. Is there a way around this, please?

@antalszava I think I figured it out. Here is the code (Your feedback will be appreciated):

import pennylane as qml
from pennylane import numpy as np

n_register = 3
dev = qml.device('default.qubit', wires=(n_register), analytic=True)
def qft_rotations(n=0):
    """Performs qft on the first n qubits in circuit (without swaps)"""
    n = int(n)
    if n == 0:
        return
    n -= 1
    qml.Hadamard(wires=n)
    for qubit in range(n):
        qml.CRZ(np.pi/2**(n-qubit), wires=[qubit, n])
    qft_rotations(n=n)

def swap_registers(n):
    for qubit in range(n//2):
        qml.SWAP(wires = [qubit, n-qubit-1])

def qft(n=0):
    """QFT on the first n qubits in circuit"""
    qft_rotations(n=n)
    swap_registers(n=n)
    return qml.probs(wires=[0, 1, 2])

circuit = qml.QNode(qft, dev)
circuit(n=3)
print(circuit.draw())

circuit(n=3)

Hi @gidu,

Also, please how can I extract the probabilities for the different bases?

qml.probs returns probabilities of computational bases states. If you’d like to extract probabilities of states in another basis, then one approach is to apply rotations to the circuit followed by using qml.probs. The needed rotations can be obtained for an observable by calling the diagonalizing_gates method:

print(qml.Hadamard(wires=[0]).diagonalizing_gates())

[RY(-0.7853981633974483, wires=[0])]

This forum thread shares more details on how we obtain these rotations.


The code snippet seems to be using PennyLane well indeed. One thing to note, is that for n>3, qft would still only extract qml.probs(wires=[0, 1, 2]) (might be worth parametrizing the wires based on the value of n).

Hi @antalszava, thank you very much for your help and suggestions so far. I find the last point you raised very important and I would like to implement it in my code. Please could you give an example or share a link where I can find something that relates to that? I would like to be able to pass the circuit as a parameter to account for variations in the wires. Thank you once again.

Hi @gidu,

Sure thing! You could try going with qml.probs(wires=range(n)) . This will create the indices based on the definition of n. Note, that it assumes a trivial wire indexing (that is, there are no custom wire labels and wires are indexed from 0). (This would become important if you’d create a device with specific wire labels.)

Hope this helps!

1 Like