# 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
def bv_algorithm_alternative (s):
"""
An alternative version of the Berstein-Varirani algorithm which contains
only NOT gates in the oracle
Inputs:
s (a string): the secret string in the oracle that needs to be decoded
Returns:
circuit: a qiskit circuit with the algorithm implemented
"""
# Get the length of the string
= len(s)
n
# Need n qubits. n are mapped to the n classical bits
= QuantumRegister(n)
q # Need n classical bits
= ClassicalRegister(n)
c # creates a quantum circuit that maps the result of a qubit
# to a classical bit
= QuantumCircuit(q, c)
circuit
circuit.barrier()# enumerate returns the index and value for each element in the string
# Reversed because of the way qiskit displays the results
# Note though that s is only referenced one time
# The number of queries to s is just 1
for i, bit in enumerate(reversed(s)):
# If the current bit is a one, then add a not gate to that qubit
if bit == '1':
# i is the control qubit, n is the target qubit
circuit.x(i) # just a visual aid for now
circuit.barrier()
# measure the qubits indexed from 0 to n-1 and store them into the
# classical bits indexed 0 to n-1
range(n), range(n))
circuit.measure(
# Return the circuit
return circuit
Simulating More Complicated Quantum Circuits
Problem 1
An algorithm with the same end result as the Bernstein-Varirani algorithm can be created using an oracle made only of NOT gates. Create this oracle and prove it works by decoding a secret string of length 3, a secret string of length 5, and a secret string of length 7.
# Test the circuit with secret string of length 3, then length 5, and finally length 7
= bv_algorithm_alternative('100')
circuit_3 = bv_algorithm_alternative('10010')
circuit_5 = bv_algorithm_alternative('1001001') circuit_7
# Simulate the circuit, all results should give us the "secret string"
= AerSimulator()
simulator = simulator.run(circuit_3).result().get_counts()
results # Print the secret string and the results of the simulation
print("Known Secret String:", '100')
print("Secret String from Algorithm:", results)
Known Secret String: 100
Secret String from Algorithm: {'100': 1024}
# Simulate the circuit, all results should give us the "secret string"
= AerSimulator()
simulator = simulator.run(circuit_5).result().get_counts()
results # Print the secret string and the results of the simulation
print("Known Secret String:", '10010')
print("Secret String from Algorithm:", results)
Known Secret String: 10010
Secret String from Algorithm: {'10010': 1024}
# Simulate the circuit, all results should give us the "secret string"
= AerSimulator()
simulator = simulator.run(circuit_7).result().get_counts()
results # Print the secret string and the results of the simulation
print("Known Secret String:", '1001001')
print("Secret String from Algorithm:", results)
Known Secret String: 1001001
Secret String from Algorithm: {'1001001': 1024}
Problem 2
Using the quantum Fourier transform algorithm developed in class, create a quantum circuit that will perform a QFT on the following data points. Show the results in terms of Bloch sphere representations. Remember you can use the bin
function in Python to convert a number to its binary representation. * x = 5 * x = 10 * x = 21 * x = 35 * x = 2
# 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
# Define pi
= np.pi
pi
## The below three functions are taken from the code for lecture 10
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
# 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
# Print out the binary form of each number, and then the length of the binary string to see how many qubits
# Remove two from the length of the binary representation to account for the "0b" leading the binary representation
print("Binary Version of 5:", bin(5), "Number of Digits:", len(bin(5))-2)
print("Binary Version of 10:", bin(10), "Number of Digits:", len(bin(10))-2)
print("Binary Version of 10:", bin(21), "Number of Digits:", len(bin(21))-2)
print("Binary Version of 10:", bin(35), "Number of Digits:", len(bin(35))-2)
print("Binary Version of 10:", bin(2), "Number of Digits:", len(bin(2))-2)
Binary Version of 5: 0b101 Number of Digits: 3
Binary Version of 10: 0b1010 Number of Digits: 4
Binary Version of 10: 0b10101 Number of Digits: 5
Binary Version of 10: 0b100011 Number of Digits: 6
Binary Version of 10: 0b10 Number of Digits: 2
# Create a general function that will work for all the numbers
def qft_on_number (num):
"""
Performs quantum Fourier transform on a number by first converting it
to a binary representation
Inputs:
num (an int): the number to perform the quantum Fourier transform on
Returns:
qc (a Qiskit circuit): the qiskit circuit which will perform the
quantum Fourier transform
"""
# Get the length of the binary number
= len(bin(num))-2
n # Get the binary representation without the leading "0b"
= bin(num)[2:]
bin_rep
# Create a quantum circuit that has as many qubits and classical bits as there
# are binary digits in the number
= QuantumRegister(n)
q = ClassicalRegister(n)
c = QuantumCircuit(q,c)
qc
# If there is a one in the binary representation then add a not gate to the corresponding
# qubit
# Reverse the binary representation before doing this for the same reasons as it is done
# for the BV algorithm
for i, bit in enumerate(reversed(bin_rep)):
# If the current bit is a one, then add a not gate to that qubit
if bit == '1':
# i is the control qubit, n is the target qubit
qc.x(i)
# Measure and simulate the algorithm just to ensure we have the correct output
qc.measure(q,c)print(qc.draw())
= AerSimulator()
simulator = simulator.run(qc).result().get_counts()
results print(num, "in Binary:", bin(num)[2:])
print("Result of Converting", num, "to Binary with QC:", results)
print("Confirm Circuit Gives Correst Result:", list(results.keys())[0]==bin(num)[2:])
# Remove the measurements before performing the quantum Fourier transform
# Do the quantum fourier transform and then return the circuit
qc.remove_final_measurements()
qft(qc, n)return qc
## x = 5
= qft_on_number(5)
qc = Statevector(qc)
statevector plot_bloch_multivector(statevector)
┌───┐ ┌─┐
q46_0: ┤ X ├───┤M├───
└───┘┌─┐└╥┘
q46_1: ─────┤M├─╫────
┌───┐└╥┘ ║ ┌─┐
q46_2: ┤ X ├─╫──╫─┤M├
└───┘ ║ ║ └╥┘
c45: 3/══════╩══╩══╩═
1 0 2
5 in Binary: 101
Result of Converting 5 to Binary with QC: {'101': 1024}
Confirm Circuit Gives Correst Result: True
## x = 10
= qft_on_number(10)
qc = Statevector(qc)
statevector plot_bloch_multivector(statevector)
┌─┐
q47_0: ─────┤M├─────────
┌───┐└╥┘ ┌─┐
q47_1: ┤ X ├─╫────┤M├───
└───┘ ║ ┌─┐└╥┘
q47_2: ──────╫─┤M├─╫────
┌───┐ ║ └╥┘ ║ ┌─┐
q47_3: ┤ X ├─╫──╫──╫─┤M├
└───┘ ║ ║ ║ └╥┘
c46: 4/══════╩══╩══╩══╩═
0 2 1 3
10 in Binary: 1010
Result of Converting 10 to Binary with QC: {'1010': 1024}
Confirm Circuit Gives Correst Result: True
## x = 21
= qft_on_number(21)
qc = Statevector(qc)
statevector plot_bloch_multivector(statevector)
┌───┐ ┌─┐
q48_0: ┤ X ├──────┤M├──────
└───┘┌─┐ └╥┘
q48_1: ─────┤M├────╫───────
┌───┐└╥┘ ║ ┌─┐
q48_2: ┤ X ├─╫─────╫─┤M├───
└───┘ ║ ┌─┐ ║ └╥┘
q48_3: ──────╫─┤M├─╫──╫────
┌───┐ ║ └╥┘ ║ ║ ┌─┐
q48_4: ┤ X ├─╫──╫──╫──╫─┤M├
└───┘ ║ ║ ║ ║ └╥┘
c47: 5/══════╩══╩══╩══╩══╩═
1 3 0 2 4
21 in Binary: 10101
Result of Converting 21 to Binary with QC: {'10101': 1024}
Confirm Circuit Gives Correst Result: True
## x = 35
= qft_on_number(35)
qc = Statevector(qc)
statevector plot_bloch_multivector(statevector)
┌───┐ ┌─┐
q49_0: ┤ X ├─────────┤M├──────
├───┤ └╥┘┌─┐
q49_1: ┤ X ├──────────╫─┤M├───
└───┘┌─┐ ║ └╥┘
q49_2: ─────┤M├───────╫──╫────
└╥┘┌─┐ ║ ║
q49_3: ──────╫─┤M├────╫──╫────
║ └╥┘┌─┐ ║ ║
q49_4: ──────╫──╫─┤M├─╫──╫────
┌───┐ ║ ║ └╥┘ ║ ║ ┌─┐
q49_5: ┤ X ├─╫──╫──╫──╫──╫─┤M├
└───┘ ║ ║ ║ ║ ║ └╥┘
c48: 6/══════╩══╩══╩══╩══╩══╩═
2 3 4 0 1 5
35 in Binary: 100011
Result of Converting 35 to Binary with QC: {'100011': 1024}
Confirm Circuit Gives Correst Result: True
## x = 2
= qft_on_number(2)
qc = Statevector(qc)
statevector plot_bloch_multivector(statevector)
┌─┐
q50_0: ─────┤M├───
┌───┐└╥┘┌─┐
q50_1: ┤ X ├─╫─┤M├
└───┘ ║ └╥┘
c49: 2/══════╩══╩═
0 1
2 in Binary: 10
Result of Converting 2 to Binary with QC: {'10': 1024}
Confirm Circuit Gives Correst Result: True
Problem 3
Without using the quantum Fourier transform algorithm we created in class (i.e. without using the recursive formalism), create a quantum circuit that will perform a quantum Fourier transform algorithm on a four-qubit state. Compare the results to relevant examples in Problem #2.
# Create a function that implements the quantum fourier transform algorithm on a four
# qubit system without using recursion
def qft_4_qubit (circuit):
# First apply a Hadamard gate to the highest indexed qubit (3)
3)
circuit.h(
# Then add relative phase gates to the target (index 3 qubit) from each
# of the other qubits
/2**(3-0), 0, 3)
circuit.cp(pi/2**(3-1), 1, 3)
circuit.cp(pi/2**(3-2), 2, 3)
circuit.cp(pi
# Now apply a Hadamard gate to the index 2 qubit
2)
circuit.h(
# And relative phase gates from the index 2 qubit to the index 0 and 1 qubits
/2**(2-0), 0, 2)
circuit.cp(pi/2**(2-1), 1, 2)
circuit.cp(pi
# Now a Hadamard gate on the index 1 qubit
1)
circuit.h(
# There is only one controlled phase gate now
/2**(1-0), 0, 1)
circuit.cp(pi
# Finally, a Hadamard gate to the index 0 (lowest indexed) qubit)
0)
circuit.h(
# Swap the order of the qubits before returning the circuit
0,3)
circuit.swap(1,2)
circuit.swap(
return circuit
# In problem 2, x = 10 has a four digit binary representation, so let's test that
# First set up the quantum circuit which creates 10, this uses the same logic from
# Problem 2
= 10
num = len(bin(num))-2
n = bin(num)[2:]
bin_rep = QuantumRegister(n)
q = ClassicalRegister(n)
c = QuantumCircuit(q,c)
qc
for i, bit in enumerate(reversed(bin_rep)):
# If the current bit is a one, then add a not gate to that qubit
if bit == '1':
# i is the control qubit, n is the target qubit
qc.x(i)
# Apply our new QFT function to this circuit and display the results as a Bloch representatiobn
= qft_4_qubit(qc)
qc = Statevector(qc)
statevector plot_bloch_multivector(statevector)
# Compare to the results from Problem 2 which uses the recursive formula
# They are the same!
= qft_on_number(10)
qc = Statevector(qc)
statevector plot_bloch_multivector(statevector)
┌─┐
q45_0: ─────┤M├─────────
┌───┐└╥┘ ┌─┐
q45_1: ┤ X ├─╫────┤M├───
└───┘ ║ ┌─┐└╥┘
q45_2: ──────╫─┤M├─╫────
┌───┐ ║ └╥┘ ║ ┌─┐
q45_3: ┤ X ├─╫──╫──╫─┤M├
└───┘ ║ ║ ║ └╥┘
c44: 4/══════╩══╩══╩══╩═
0 2 1 3
10 in Binary: 1010
Result of Converting 10 to Binary with QC: {'1010': 1024}
Confirm Circuit Gives Correst Result: True