# Needed to set up the quantum circuit
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
# Needed to simulate running a quantum computer
from qiskit_aer import AerSimulator
# Neded to visualize the results of running a quantum computer
from qiskit.visualization import plot_histogram
# Needed for vectors and stuff
import numpy as np
# This import let's us visualize the Qiskit qubits as state vectors (bra-ket notation)
from qiskit.quantum_info import Statevector
# This import let's us visualize
from qiskit.visualization import plot_bloch_multivector
More Quantum Fourier Transforms, Quantum Phase Estimation, and Quantum Parallelism
A Few Final QFT Topics
def qft_rotations(circuit, n):
"""
Performs qft on the first n qubits in circuit (without swaps)
Inputs:
circuit is the qiskit circuit
n is the number of qubits in the circuit
Returns
circuit after adding Hadamard gates and controlled phase gates to perform
the qft
"""
# Break condition
if n == 0:
return circuit
# Define pi
= np.pi
pi # Subtract 1 because the highest indexed qubit is one less than the total number of qubits
-= 1
n # Hadamard to the highest indexed qubit
circuit.h(n)# Controlled phase gate with the qubit at n (the highest ordered) being the target and every
# lower indexed qubit being the control in order
for qubit in range(n):
= pi/2**(n-qubit)
phi
circuit.cp(phi, qubit, n)
# Recursion time!
# Recall the function with the same circuit, but n is one less than it was originally
# The next run-through will ignore the highest indexed qubit in this run-through
qft_rotations(circuit, n)
def swap_qubits(circuit, n):
"""
Performs swaps the locations of all qubits
Inputs:
circuit is the qiskit circuit
n is the number of qubits in the circuit
Returns
circuit after swapping all qubits
"""
# Int in range function because range requires an integer
for qubit in range(int(n/2)):
# swap the position of a low-indexed qubit and the qubit
# that is the same distance from the highest ordered qubit
-qubit-1)
circuit.swap(qubit, nreturn circuit
def qft(circuit, n):
"""
Performs QFT on a qiskit circuit by adding the correct gates to the circuit and then
swapping the order of the qubits
Inputs:
circuit is the qiskit circuit
n is the number of qubits in the circuit
Returns:
circuit is the qiskit circuit after the qft gates are applied
"""
qft_rotations(circuit, n)
swap_qubits(circuit, n)return circuit
## Example!
= QuantumRegister(3)
q = ClassicalRegister(3)
c = QuantumCircuit(q,c)
qc
# Encode the state 5
0)
qc.x(2)
qc.x(
print(qc.draw())
= Statevector(qc)
statevector plot_bloch_multivector(statevector)
┌───┐
q1_0: ┤ X ├
└───┘
q1_1: ─────
┌───┐
q1_2: ┤ X ├
└───┘
c0: 3/═════
3)
qft(qc, qc.draw()
┌───┐ ┌───┐ q1_0: ┤ X ├──────■──────────────────────■───────┤ H ├─X─ └───┘ │ ┌───┐ │P(π/2) └───┘ │ q1_1: ───────────┼────────■───────┤ H ├─■─────────────┼─ ┌───┐┌───┐ │P(π/4) │P(π/2) └───┘ │ q1_2: ┤ X ├┤ H ├─■────────■───────────────────────────X─ └───┘└───┘ c0: 3/══════════════════════════════════════════════════
# Bloch sphere diagram
# no measurements allowed
qc.remove_final_measurements() = Statevector(qc)
statevector plot_bloch_multivector(statevector)
## Can also perform QFT using the built-in function
from qiskit.circuit.library import QFT
# Create a 3-qubit Quantum Circuit
= QuantumRegister(3)
q = ClassicalRegister(3)
c = QuantumCircuit(q,c)
qc
# Encode the binary 5 to compare to out results
0)
qc.x(2)
qc.x(
# Apply a QFT on the qubits
= QFT(num_qubits=3)
qft_circuit range(3))
qc.append(qft_circuit.to_instruction(),
# Draw the circuit
='mpl') qc.draw(output
# Same Results!
= Statevector(qc)
statevector plot_bloch_multivector(statevector)
# Apply an Inverse QFT on the qubits of the same circuit
= QFT(num_qubits=3, inverse=True)
qft_circuit range(3))
qc.append(qft_circuit.to_instruction(),
# Draw the circuit
='mpl') qc.draw(output
# The result should be a binary 5!
= Statevector(qc)
statevector plot_bloch_multivector(statevector)
Quantum Phase Estimation
## Let's start off easy. Our controlled U gate is just the
# controlled phase gate with some specific angle.
= 2*np.pi/3
phi
## Let's start by measuring the precision of the eigenvalue to three decimal places,
## so we need 4 qubits mapping to three classical bits (we do not measure
## the eigenvector qubit)
= QuantumRegister(4)
q = ClassicalRegister(3)
c = QuantumCircuit(q,c)
qc
## First we need to make sure the last qubit is in an eigenstate of the U gate.
## The up and down states are both possible eigenvectors but up is trivial
## (has an eigenvalue of 1) so let's go with down
3)
qc.x(
qc.barrier()
## Now Hadamard gates for the other qubits
0)
qc.h(1)
qc.h(2)
qc.h(
qc.barrier()
## Controlled U gate from eigenvector qubit (target) to highest indexed H gated qubit
2, 3)
qc.cp(phi,
qc.barrier()
## Now two controlled U gates from eigenvector qubit (target)
## to second highest indexed H gated qubit
1, 3)
qc.cp(phi, 1, 3)
qc.cp(phi,
qc.barrier()
## Finally four controleld U gates from eigenvector qubit target)
## to lowest index H gated qubit
0, 3)
qc.cp(phi, 0, 3)
qc.cp(phi, 0, 3)
qc.cp(phi, 0, 3)
qc.cp(phi,
qc.barrier()
## Finally the IQFT but only on the first three qubits
= qc.compose(QFT(3, inverse=True), [0,1,2])
qc
qc.barrier()
# Measure of course!
for n in range(3):
qc.measure(n,n)
qc.barrier()
="mpl") qc.draw(output
# importing Qiskit transpile
from qiskit import transpile
= AerSimulator()
simulator # Number of times to simulate
= 2048
shots # Need to transpile since using the IQFT "black box" function
# transpile converts the black box into its invividual gates
= transpile(qc, simulator)
t_qpe
= simulator.run(t_qpe, shots=shots).result()
results = results.get_counts()
answer
plot_histogram(answer)
/Users/butlerju/Library/Python/3.9/lib/python/site-packages/urllib3/__init__.py:35: NotOpenSSLWarning: urllib3 v2 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'LibreSSL 2.8.3'. See: https://github.com/urllib3/urllib3/issues/3020
warnings.warn(
## First example where there is more than one possible answer due
## to the Hadamard gates and quantum parallelism, but most likely
## answer is the one with the higest number of results
print("Known Angle:", phi)
print("Most Likely Result:", 2*np.pi*(0/2+1/4+1/8))
print("Also Likely:", 2*np.pi*(0/2+1/4+0/8))
## To get better results, add more counting qubits!
Known Angle: 2.0943951023931953
Most Likely Result: 2.356194490192345
Also Likely: 1.5707963267948966
## WARNING: This starts to really chug above 10 qubits
## (remember this is a simulated quantum computer,
# not a real one)
## Let's start off easy. Our controlled U gate is just the controlled
## phase gate with some specific angle.
= np.pi/3
phi
## Make it general now to work with any number of qubits
= 5
counting_qubits
# Need one more qubit than bit
= QuantumRegister(counting_qubits + 1)
q = ClassicalRegister(counting_qubits)
c = QuantumCircuit(q,c)
qc
## First we need to make sure the last qubit is in an eigenstate of the U gate.
## The up and down states are both possible eigenvectors but up is trivial
## (has an eigenvalue of 1) so let's go with down
qc.x(counting_qubits)
qc.barrier()
# Apply H-Gates to counting qubits:
for qubit in range(counting_qubits):
qc.h(qubit)
qc.barrier()
# Do the controlled-U operations:
= 1
repetitions for control in range(counting_qubits):
for i in range(repetitions):
qc.cp(phi, control, counting_qubits)
qc.barrier()*= 2
repetitions
# Do the inverse QFT:
= qc.compose(QFT(counting_qubits, inverse=True), range(counting_qubits))
qc
qc.barrier()
# Measure of course!
for n in range(counting_qubits):
qc.measure(n,n)
qc.draw()
░ ┌───┐ ░ ░ ░ » q10_0: ──────░─┤ H ├─░──■────────░────────────────────░───────────────────» ░ ├───┤ ░ │ ░ ░ » q10_1: ──────░─┤ H ├─░──┼────────░──■────────■────────░───────────────────» ░ ├───┤ ░ │ ░ │ │ ░ » q10_2: ──────░─┤ H ├─░──┼────────░──┼────────┼────────░──■────────■───────» ░ ├───┤ ░ │ ░ │ │ ░ │ │ » q10_3: ──────░─┤ H ├─░──┼────────░──┼────────┼────────░──┼────────┼───────» ░ ├───┤ ░ │ ░ │ │ ░ │ │ » q10_4: ──────░─┤ H ├─░──┼────────░──┼────────┼────────░──┼────────┼───────» ┌───┐ ░ └───┘ ░ │P(π/3) ░ │P(π/3) │P(π/3) ░ │P(π/3) │P(π/3) » q10_5: ┤ X ├─░───────░──■────────░──■────────■────────░──■────────■───────» └───┘ ░ ░ ░ ░ » c3: 5/═══════════════════════════════════════════════════════════════════» » « ░ » «q10_0: ───────────────────░──────────────────────────────────────────────» « ░ » «q10_1: ───────────────────░──────────────────────────────────────────────» « ░ » «q10_2: ─■────────■────────░──────────────────────────────────────────────» « │ │ ░ » «q10_3: ─┼────────┼────────░──■────────■────────■────────■────────■───────» « │ │ ░ │ │ │ │ │ » «q10_4: ─┼────────┼────────░──┼────────┼────────┼────────┼────────┼───────» « │P(π/3) │P(π/3) ░ │P(π/3) │P(π/3) │P(π/3) │P(π/3) │P(π/3) » «q10_5: ─■────────■────────░──■────────■────────■────────■────────■───────» « ░ » « c3: 5/══════════════════════════════════════════════════════════════════» « » « ░ » «q10_0: ────────────────────────────░─────────────────────────────────────» « ░ » «q10_1: ────────────────────────────░─────────────────────────────────────» « ░ » «q10_2: ────────────────────────────░─────────────────────────────────────» « ░ » «q10_3: ─■────────■────────■────────░─────────────────────────────────────» « │ │ │ ░ » «q10_4: ─┼────────┼────────┼────────░──■────────■────────■────────■───────» « │P(π/3) │P(π/3) │P(π/3) ░ │P(π/3) │P(π/3) │P(π/3) │P(π/3) » «q10_5: ─■────────■────────■────────░──■────────■────────■────────■───────» « ░ » « c3: 5/══════════════════════════════════════════════════════════════════» « » « » «q10_0: ───────────────────────────────────────────────────────────────» « » «q10_1: ───────────────────────────────────────────────────────────────» « » «q10_2: ───────────────────────────────────────────────────────────────» « » «q10_3: ───────────────────────────────────────────────────────────────» « » «q10_4: ─■────────■────────■────────■────────■────────■────────■───────» « │P(π/3) │P(π/3) │P(π/3) │P(π/3) │P(π/3) │P(π/3) │P(π/3) » «q10_5: ─■────────■────────■────────■────────■────────■────────■───────» « » « c3: 5/═══════════════════════════════════════════════════════════════» « » « ░ ┌──────────┐ ░ ┌─┐ » «q10_0: ──────────────────────────────────────────────░─┤0 ├─░─┤M├───» « ░ │ │ ░ └╥┘┌─┐» «q10_1: ──────────────────────────────────────────────░─┤1 ├─░──╫─┤M├» « ░ │ │ ░ ║ └╥┘» «q10_2: ──────────────────────────────────────────────░─┤2 IQFT_dg ├─░──╫──╫─» « ░ │ │ ░ ║ ║ » «q10_3: ──────────────────────────────────────────────░─┤3 ├─░──╫──╫─» « ░ │ │ ░ ║ ║ » «q10_4: ─■────────■────────■────────■────────■────────░─┤4 ├─░──╫──╫─» « │P(π/3) │P(π/3) │P(π/3) │P(π/3) │P(π/3) ░ └──────────┘ ░ ║ ║ » «q10_5: ─■────────■────────■────────■────────■────────░──────────────░──╫──╫─» « ░ ░ ║ ║ » « c3: 5/════════════════════════════════════════════════════════════════╩══╩═» « 0 1 » « «q10_0: ───────── « «q10_1: ───────── « ┌─┐ «q10_2: ┤M├────── « └╥┘┌─┐ «q10_3: ─╫─┤M├─── « ║ └╥┘┌─┐ «q10_4: ─╫──╫─┤M├ « ║ ║ └╥┘ «q10_5: ─╫──╫──╫─ « ║ ║ ║ « c3: 5/═╩══╩══╩═ « 2 3 4
# Run the simulation
= AerSimulator()
simulator = 2048
shots = transpile(qc, simulator)
t_qpe = simulator.run(t_qpe, shots=shots).result()
results = results.get_counts() answer
# Sort the returned dictionary in order of counts of each result
= sorted(answer, key=answer.get, reverse=True) sorted_answer
# Write a little function to convert the output of the quantum circuit
# to an angle
##############################
## CONVERT TO ANGLE ##
##############################
def convert_to_angle(b):
"""
Converts a binary string to an angle using the quantum phase estimation
algorithm
Inputs:
b (a binary string)
Returns:
angle (a float): the corresponding angle
"""
sum = 0
for i in range(len(b)):
sum += int(b[i])/2**(i+1)
= sum*2*np.pi
angle return angle
## NOTE: Since we are using the controlled phase gate, we only need to look at the angle
## In general, the eigenvalue is e^{i\theta}, not just the angle
print("Known Angle:", phi)
print()
print("Most Likely Angle:", convert_to_angle(sorted_answer[0]))
print("Probability:", answer[sorted_answer[0]]/shots)
print()
print("Second Most Likely Angle:", convert_to_angle(sorted_answer[1]))
print("Probability:", answer[sorted_answer[1]]/shots)
print()
print("Third Most Likely Angle:", convert_to_angle(sorted_answer[2]))
print("Probability:", answer[sorted_answer[2]]/shots)
print()
Known Angle: 1.0471975511965976
Most Likely Angle: 0.9817477042468103
Probability: 0.6904296875
Second Most Likely Angle: 1.1780972450961724
Probability: 0.15771484375
Third Most Likely Angle: 0.7853981633974483
Probability: 0.04443359375