# Quantum phase estimation

Your task in this notebook is to implement the quantum Fourier transform on a set of 3 qubits in Qiskit, and then use it to estimate the eigenvalue of a simple Hamiltonian.

### Part A: Implementing the QFT


```python
import numpy as np

from qiskit import QuantumRegister, ClassicalRegister
from qiskit import QuantumCircuit
from qiskit import execute, BasicAer

import qiskit.tools.visualization as qvis

import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning) 
```

**Task 1.** In the lecture slides I showed the QFT in general; using this template, write out the circuit for the 3-qubit case. Don't forget the SWAP at the end!  

** A. ** _Here's a graphic of the circuit._



**Task 2.** Recall that the rotations have the form
\begin{equation}
 R_k = \begin{pmatrix}
       1 & 0 \\ 0 & e^{2\pi i / 2^k}
       \end{pmatrix}
\end{equation}
Evaluate the unitary operations for the rotations present in Task 1. Do these gates look familiar?

** A. ** _These are the $S$ and $T$ gates that I showed in the slides!_

** Task 3.** Now it's time to implement the circuit. I've set up a 3-qubit quantum register, so all you have to do is implement the gates. For reference, [this page](https://qiskit.org/documentation/terra/summary_of_quantum_operations.html#controlled-operations-on-qubits) contains information on the gates you can implement natively in Qiskit. Hint: look at the `cu1` gates.


```python
# Empty register
q = QuantumRegister(3)

# This object will be our circuit
qft = QuantumCircuit(q)

# Apply gates to the first qubit
qft.h(q[0])
qft.cu1(np.pi/2, q[1], q[0])
qft.cu1(np.pi/4, q[2], q[0])

# Apply gates to the second qubit
qft.h(q[1])
qft.cu1(np.pi/2, q[2], q[1])

# Apply  remaining gates
qft.h(q[2])

qft.swap(q[0], q[2])
```

### Part B: Eigenvalue estimation

In this part, we're going to implement the phase estimation algorithm to get the eigenvalue of the following unitary:
\begin{equation}
 U = \begin{pmatrix}
       1 & 0 \\ 
       0 & e^{5\pi i / 4} \\
       \end{pmatrix}
\end{equation}

** Task 4. ** What is the eigenvalue $\varphi$ of this matrix as expressed in the phase estimation problem, i.e. $e^{2\pi i \varphi}$? What is its expansion in the notation $0.\varphi_1 \cdots \varphi_n$?

** A. ** The eigenvalue here is $\varphi = 0.625$. In decimal notation this is $0.101$, i.e. $1 \cdot (2^{-1}) + 0 \cdot 2^{-2} + 1 \cdot 2^{-3}$. This fits perfectly in a 3-qubit state, and we should expect to measure $|101\rangle$ as our output state after running the algorithm. 

Now let's set up the two quantum registers: one for the 3 qubits to represent phase, and 2 for the eigenvector since it is a 4-dimensional vector. We'll also need 3 classical bits to hold the measurement outcomes of the phase register.


```python
ph_reg = QuantumRegister(3)
eig_reg = QuantumRegister(1)
meas_reg = ClassicalRegister(3)

qpe = QuantumCircuit(ph_reg, eig_reg, meas_reg)
```

** Task 5. ** Apply the phase estimation algorithm to your registers. Recall that you'll also need to initialize the eigenvector register into the proper eigenvector.


```python
# Initialize the eigenvector
qpe.x(eig_reg)

# Perform the phase estimation; we're applying a controlled U varying amounts of times
qpe.h(ph_reg)

U_phase = 2 * np.pi * 0.625 

qpe.cu1(U_phase, ph_reg[2], eig_reg[0])

qpe.cu1(U_phase, ph_reg[1], eig_reg[0])
qpe.cu1(U_phase, ph_reg[1], eig_reg[0])

qpe.cu1(U_phase, ph_reg[0], eig_reg[0])
qpe.cu1(U_phase, ph_reg[0], eig_reg[0])
qpe.cu1(U_phase, ph_reg[0], eig_reg[0])
qpe.cu1(U_phase, ph_reg[0], eig_reg[0])
```

** Task 6. ** Apply the inverse QFT to your phase register.

Note: unfortunately, Qiskit does not yet support a simple circuit reversal operation; so you'll have to manually list the inverses of each gate. Don't forget to take the adjoint!


```python
qpe.swap(ph_reg[0], ph_reg[2])
qpe.h(ph_reg[2])
qpe.cu1(-np.pi/2, ph_reg[2], ph_reg[1])
qpe.h(ph_reg[1])
qpe.cu1(-np.pi/4, ph_reg[2], ph_reg[0])
qpe.cu1(-np.pi/2, ph_reg[1], ph_reg[0])
qpe.h(ph_reg[0])
```

** Final task. ** Let's simulate the circuit and measure to get the vector corresponding to our phase!


```python
qpe.measure(ph_reg, meas_reg)

backend = BasicAer.get_backend('qasm_simulator') # the device to run on
result = execute(qpe, backend, shots=1000).result()
counts = result.get_counts(qpe)

# Extract the counts and compute the value
eigenvalue = 0

# Reverse - in this case it is symmetric, but in general you'll have to be careful
binary_eigenvalue = list(counts.keys())[0] 
binary_eigenvalue = binary_eigenvalue[::-1]

for idx, b in enumerate(list(binary_eigenvalue)):
    if b == '1':
        eigenvalue += 2 ** (-(idx + 1))

print(f"Phase estimation routine returned the eigenvalue {eigenvalue}.")
```

84ff879c13ccd4909b689b4e7092630655f5a77f
ipynb
Jupyter Notebook
02-gate-model-applications/notebooks/Solved-Quantum-Phase-Estimation.ipynb
a-capra/Intro-QC-TRIUMF
9738e6a49f226367247cf7bc05a00751f7bf2fe5
MIT
2019-05-09T17:40:20.000Z
2021-12-15T12:23:17.000Z
02-gate-model-applications/notebooks/Solved-Quantum-Phase-Estimation.ipynb
a-capra/Intro-QC-TRIUMF
9738e6a49f226367247cf7bc05a00751f7bf2fe5
MIT
2021-09-29T07:34:09.000Z
2021-09-29T21:01:29.000Z
02-gate-model-applications/notebooks/Solved-Quantum-Phase-Estimation.ipynb
a-capra/Intro-QC-TRIUMF
9738e6a49f226367247cf7bc05a00751f7bf2fe5
MIT
2019-05-09T18:45:49.000Z
2021-12-15T12:23:21.000Z
Qwen/Qwen-72B
1. YES
2. YES