import matplotlib.pyplot as plt
import numpy as np
from typing import *
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector, Operator
from qiskit_aer import AerSimulator
sim = AerSimulator()
from util import zero, one, qft
Shor’s Part 3/5: Modular Exponentiation#
References
Introduction to Classical and Quantum Computing, Chapter 7.9.3
Introduction to Quantum Information Science: Lecture 19 by Scott Aaronson
Quantum Computation and Quantum Information: Chapter 5, Nielsen and Chuang
Quantum Oracle for Modular Multiplication#
Suppose we have a unitary
so that \(U_f\) encodes modular multiplication by \(a\). This oracle will be useful for quantum order finding.
Question: why don’t we need an xor oracle?
Example#
Consider
where \(a = 7\) and \(N = 15\).
a = 7; N = 15
xs = [x for x in range(N)]
ys = [(a ** x) % N for x in xs]
plt.plot(xs, ys, marker='x'); plt.title("a=7 and N=15")
Text(0.5, 1.0, 'a=7 and N=15')
Quantum Oracle#
Let \(y = y_4 y_3 y_2 y_1\) be the binary expansion of \(y\) in 4 bits where \(y_4\) is the most significant bit.
Our goal is to construct the unitary
where \(7 = 0111\) and \(15 = 1111\).
def amod15(a, power):
# Adapted From: https://qiskit.org/textbook/ch-algorithms/shor.html
if a not in [2,4,7,8,11,13]:
raise ValueError("'a' must be 2,4,7,8,11 or 13")
U = QuantumCircuit(4)
for iteration in range(power):
if a in [2,13]:
U.swap(2,3); U.swap(1,2); U.swap(0,1)
if a in [7,8]:
U.swap(0,1); U.swap(1,2); U.swap(2,3)
if a in [4, 11]:
U.swap(1,3); U.swap(0,2)
if a in [7,11,13]:
for q in range(4):
U.x(q)
U = U.to_gate(); U.name = "%i^%i mod 15" % (a, power);
return U
qc_amod15 = QuantumCircuit(4)
qc_amod15.append(amod15(7, 2**0), [0, 1, 2, 3])
U_f = Operator(qc_amod15)
qc_amod15.draw(output="mpl", style="iqp")
print((1 * 7) % 15) # Check that we get binary 7 (little endian)
(zero ^ zero ^ zero ^ one).evolve(U_f).draw("latex")
7
print((3 * 7) % 15) # Check that we get binary 6 (little endian)
(zero ^ zero ^ one ^ one).evolve(U_f).draw("latex")
6
Iterating Applications of \(U_f\)#
Observe that if we iteratively apply \(U_f\) to itself
Thus we can encode the original function as
so that we can sequence \(U_f\) \(x\) times to simulate \(f(x)\).
# Starting vector (reminder: little endian)
(zero ^ zero ^ zero ^ one).draw("latex")
# U_f |0001> (reminder: little endian)
(zero ^ zero ^ zero ^ one).evolve(U_f).draw("latex")
U_f2 = U_f.compose(U_f)
# U_f^2 |0001> (reminder: little endian)
(zero ^ zero ^ zero ^ one).evolve(U_f2).draw("latex")
U_f3 = U_f.compose(U_f2)
# U_f^3 |0001> (reminder: little endian)
(zero ^ zero ^ zero ^ one).evolve(U_f3).draw("latex")
U_f4 = U_f.compose(U_f3)
# U_f^4 |0011> = |0001> (reminder: little endian)
# Recall that the order was 4
(zero ^ zero ^ zero ^ one).evolve(U_f4).draw("latex")
Aside: Eigenvectors and Eigenvalues#
We say that \(x\) is an eigenvector and \(\lambda\) is an eigenvalue of a matrix \(A\) if
Eigenvectors and eigenvalues of \(U_f\)#
\(|0001\rangle\) is an eigenvector of \(U_f\) with eigenvalue \(1\).
Are there other eigenvectors/eigenvalues of \(U_f\)?
Let
be an equal superposition of the powers of \(U_f\). Then
(1/2.*((zero ^ zero ^ zero ^ one).evolve(U_f) +
(zero ^ zero ^ zero ^ one).evolve(U_f2) +
(zero ^ zero ^ zero ^ one).evolve(U_f3) +
(zero ^ zero ^ zero ^ one))).draw("latex")
# Checking eigenvector
(1/2.*((zero ^ zero ^ zero ^ one).evolve(U_f) +
(zero ^ zero ^ zero ^ one).evolve(U_f2) +
(zero ^ zero ^ zero ^ one).evolve(U_f3) +
(zero ^ zero ^ zero ^ one))).evolve(U_f).draw("latex")
More useful eigenvectors and eigenvalues of \(U_f\)?#
It is a fact that unitary matrices have unit norm eigenvalues.
Consequently, we might wonder if it possible to have phases \(e^{2\pi i k}\) as eigenvalues.
Where might we get such eigenvalues?
Let
be an equal superposition of the powers of \(U_f\) with phase shifts \(e^{-\frac{2\pi ik\ell}{s}}\) .
Then
which means we leak out information about the period \(s\) in the global phase.
This is the change-of-basis given by the QFT.
qft4 = QuantumCircuit(4)
qft4 = qft(qft4, 4)
qft4.draw(output="mpl", style="iqp")
(zero ^ zero ^ zero ^ one).evolve(U_f).evolve(qft4).draw("latex")
Final fact: Sum of \(|u_\ell \rangle\)#
One final observation is that
Summary#
We reviewed the quantum modular exponentiation algorithm.