使用QISKit量子傅里叶变换FFT,DFT

使用平台为Jupyter Notebook,Python版本3.6,QISKit版本0.6.0。

1. Implement the QFT (Quantum Fourier Transform)

This code is the modified version of QISKit Github. In this link, you can also find some basic explanation for the principle of QFT.

For more materials to learn QFT, you can check:

  1. Michael Nielsen and Isaac Chuang (2000). Quantum Computation and Quantum Information. Cambridge: Cambridge University Press. from P217
  2. Wikipedia page for Quantum Fourier Transform
import math

# importing Qiskit
from qiskit import Aer, IBMQ
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, execute
from qiskit.backends.ibmq import least_busy

# useful additional packages 
from qiskit.wrapper.jupyter import *
from qiskit.tools.visualization import plot_histogram
# Load IBM token
IBMQ.load_accounts()

import warnings  # Ignore the warning message
warnings.filterwarnings('ignore')

1.1 QFT quantum circuit and its measurement

def input_state(circ, q, n):
    """n-qubit input state for QFT that produces output 1."""
    for j in range(n):
        circ.h(q[j])
        circ.u1(math.pi/float(2**(j)), q[j]).inverse()

def qft(circ, q, n):
    """n-qubit QFT on q in circ."""
    for j in range(n):
        for k in range(j):
            circ.cu1(math.pi/float(2**(j-k)), q[j], q[k])
        circ.h(q[j])
n = 3  # No. of qubits
q = QuantumRegister(n)
c = ClassicalRegister(n)
qft_n = QuantumCircuit(q, c)

input_state(qft_n, q, n)
qft(qft_n, q, n)
for i in range(n):
    qft_n.measure(q[i], c[i])
print(qft_n.qasm())
OPENQASM 2.0;
include "qelib1.inc";
qreg q0[3];
creg c0[3];
h q0[0];
u1(-3.14159265358979) q0[0];
h q0[1];
u1(-1.57079632679490) q0[1];
h q0[2];
u1(-0.785398163397448) q0[2];
h q0[0];
cu1(1.57079632679490) q0[1],q0[0];
h q0[1];
cu1(0.785398163397448) q0[2],q0[0];
cu1(1.57079632679490) q0[2],q0[1];
h q0[2];
measure q0[0] -> c0[0];
measure q0[1] -> c0[1];
measure q0[2] -> c0[2];

Draw the quantum circuit:

# Visualizing quantum circuit
from qiskit.tools.visualization import circuit_drawer

# Create the circuit
QFT_circuit = qft_n

# Provide a name to write the diagram to the filesystem
circuit_drawer(QFT_circuit, filename='./QFT_circuit.png')

# Use the return value with show() to display the diagram
#diagram = circuit_drawer(bell_circuit)
#diagram.show()
WARNING: Unable to compile latex. Is `pdflatex` installed? Skipping latex circuit drawing...

png

1.2 Run QFT on a local simulator

This is the ideal result of running a QFT algorithm.

backend = Aer.get_backend("qasm_simulator")

simulate = execute(qft_n, backend=backend, shots=1024).result()
# shots=1024, in total we tried 1024 times
simulate.get_counts()
{'001': 1024}

We will see that for NO. of qubits = 3 the outcome is always 001 when we execute the code on the simulator, because the function input_state can make our result always 1. Without using this function, we will get the result {000, 001, 010, 011, 100, 101, 110, 111} with the same probability.

1.3 Run QFT on a remote quantum chip

# %%qiskit_job_status

# # Use the IBM Quantum Experience
# backend = least_busy(IBMQ.backends(simulator=False))

# job_exp = execute(qft3, backend=backend, shots=shots)
# see a list of available remote backends

ibmq_backends = IBMQ.backends()
print("Remote backends: ", ibmq_backends)
# Compile and run the Quantum Program on a real device backend
least_busy_device = least_busy(IBMQ.backends(simulator=False))
print("Running on current least busy device: ", least_busy_device)
# runing the job
job_exp = execute(qft_n, backend=least_busy_device, shots=1024)
Remote backends:  [<IBMQBackend('ibmqx4') from IBMQ()>, <IBMQBackend('ibmqx5') from IBMQ()>, <IBMQBackend('ibmqx2') from IBMQ()>, <IBMQBackend('ibmq_16_melbourne') from IBMQ()>, <IBMQBackend('ibmq_qasm_simulator') from IBMQ()>]
Running on current least busy device:  ibmq_16_melbourne

In most cases, the remote devices we are able to use are:

  • ibmqx4: 5-qubit backend

  • ibmq_16_melbourne: 16-qubit backend

results = job_exp.result()
plot_histogram(results.get_counts())

png

We can find out that for NO. of qubits = 3, 001 is with highest probability, which is consistent with the ideal result by using local simulator.

2. Compare DFT, FFT and QFT

The functions of DFT and FFT are from this article.

Note: the input vector length N = 2^n should be set the same for all algorithms.

2.1 Running time of DFT and QFT

# A function for DFT
import numpy as np
def DFT_slow(x):
    """Compute the discrete Fourier Transform of the 1D array x"""
    x = np.asarray(x, dtype=float)
    N = x.shape[0]
    n = np.arange(N)
    k = n.reshape((N, 1))
    M = np.exp(-2j * np.pi * k * n / N)
    return np.dot(M, x)
# A function for FFT
def FFT(x):
    """A recursive implementation of the 1D Cooley-Tukey FFT"""
    x = np.asarray(x, dtype=float)
    N = x.shape[0]
    
    if N % 2 > 0:
        raise ValueError("size of x must be a power of 2")
    elif N <= 32:  # this cutoff should be optimized
        return DFT_slow(x)
    else:
        X_even = FFT(x[::2])
        X_odd = FFT(x[1::2])
        factor = np.exp(-2j * np.pi * np.arange(N) / N)
        return np.concatenate([X_even + factor[:int(N / 2)] * X_odd,
                               X_even + factor[int(N / 2):] * X_odd])
def FFT_vectorized(x):
    """A vectorized, non-recursive version of the Cooley-Tukey FFT"""
    x = np.asarray(x, dtype=float)
    N = x.shape[0]

    if np.log2(N) % 1 > 0:
        raise ValueError("size of x must be a power of 2")

    # N_min here is equivalent to the stopping condition above,
    # and should be a power of 2
    N_min = min(N, 32)
    
    # Perform an O[N^2] DFT on all length-N_min sub-problems at once
    n = np.arange(N_min)
    k = n[:, None]
    M = np.exp(-2j * np.pi * n * k / N_min)
    X = np.dot(M, x.reshape((N_min, -1)))

    # build-up each level of the recursive calculation all at once
    while X.shape[0] < N:
        X_even = X[:, :int(X.shape[1] / 2)]
        X_odd = X[:, int(X.shape[1] / 2):]
        factor = np.exp(-1j * np.pi * np.arange(X.shape[0])
                        / X.shape[0])[:, None]
        X = np.vstack([X_even + factor * X_odd,
                       X_even - factor * X_odd])

    return X.ravel()

Time for running DFT and FFT:

‘‘Note that we still haven’t come close to the speed of the built-in FFT algorithm in numpy, and this is to be expected. The FFTPACK algorithm behind numpy’s fft is a Fortran implementation which has received years of tweaks and optimizations. Furthermore, our NumPy solution involves both Python-stack recursions and the allocation of many temporary arrays, which adds significant computation time’’–Understanding the FFT Algorithm

n = 3
N = 2**n
x = np.random.random(N) # N is the length of vector in time domain
# Compare the time
print('DFT:')
%timeit DFT_slow(x)
print('numpy FFT:')
%timeit np.fft.fft(x) # function from numpy
print('FFT:')
%timeit FFT(x)
print('vectorized FFT:')
%timeit FFT_vectorized(x)
DFT:
35.4 µs ± 1.44 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
numpy FFT:
3.34 µs ± 132 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
FFT:
36.1 µs ± 1.85 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
vectorized FFT:
40.9 µs ± 1.39 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

2.2 Running time of QFT

The function of QFT running on a local simulator:

def QFT():
    n = 3  # No. of qubits 
    q = QuantumRegister(n)
    c = ClassicalRegister(n)
    qft_n = QuantumCircuit(q, c)
    input_state(qft_n, q, n)
    qft(qft_n, q, n)
    #put barrier before measurement
    qft_n.barrier(q)
    for i in range(n):
        qft_n.measure(q[i], c[i])
    # run on a remote quantum chip
    ibmq_backends = IBMQ.backends()
    # Compile and run the Quantum Program on a real device backend
    least_busy_device = least_busy(IBMQ.backends(simulator=False))
    # runing the job
    job_exp = execute(qft_n, backend=least_busy_device, shots=1024)
def null_operation():
    n = 1  # No. of qubits 
    q = QuantumRegister(n)
    c = ClassicalRegister(n)
    qft_n = QuantumCircuit(q, c)
    qft_n.h(q[0])  # We cannot execute without doing anything on qubits, so here a one-qubit quantum gate which cost least time is added
    # qft_n.measure(q, c)
    # run on a remote quantum chip
    ibmq_backends = IBMQ.backends()
    # Compile and run the Quantum Program on a real device backend
    least_busy_device = least_busy(IBMQ.backends(simulator=False))
    # runing the job
    job_exp = execute(qft_n, backend=least_busy_device, shots=1024)

Measure the running time of QFT funtion and null operation on a remote quantum chip:

print('Run QFT on a remote quantum chip:')
%timeit QFT()
print('Run null operation on a remote quantum chip:')
%timeit null_operation()

import warnings  # Ignore the warning message
warnings.filterwarnings('ignore')

''You are just interested in the time program takes to execute on a quantum computer, you can probably make some estimates based on the circuit depth. You don’t actually need to run anything. Just take the circuit, compile it, and then add up the delays on the longest path of the compiled circuit. Single qubit gates are (ballpark) 10 ns, and two qubit gates 100 ns.

Simulation on classical hardware does not follow this pattern (it comes down to multiplying matrices on your laptop). Also even if you could run your program on a quantum computer (as you can, just have to wait a bit in queue), the execution time you get is a combination of actual runtime plus overhead of loading waveforms, etc. onto the device (the classical control). I suppose you just want the actual gate delays, so my suggestion above is probably the best bet.’’–Is it meaningful to test the speed of algorithms running on local simulator?

猜你喜欢

转载自blog.csdn.net/m0_37622530/article/details/83215945