This page was generated from the notebook
examples/notebooks/4c_Quantum_Phase_Estimation.ipynb.
[ ]:
from math import floor
import numpy as np
import numpy.typing as npt
from mpqp import (
QCircuit,
H,
CRk,
Barrier,
SWAP,
Gate,
CustomGate,
Ry,
X,
BasisMeasure,
IBMDevice,
run,
)
from mpqp.qasm.qasm_to_mpqp import qasm2_parse
from mpqp.tools.maths import normalize
Generate circuit from state
[ ]:
from qiskit import QuantumCircuit
from qiskit import qasm2
import re
def generate_circuit(state: npt.NDArray[np.complex128], nb_qubits: int):
circuit = QuantumCircuit(nb_qubits)
circuit.initialize(state, list(range(nb_qubits)))
decomposed_circuit = (
circuit.decompose().decompose().decompose().decompose().decompose()
) # weird trick, we know
qasm_code = qasm2.dumps(decomposed_circuit.reverse_bits())
cleaned_code = re.sub(r"reset q\[\d+\];\n", "", qasm_code)
circ = qasm2_parse(cleaned_code)
return circ
[4]:
print(generate_circuit(normalize(np.array([2,5,4,6])), 2))
┌───────────────┐
q_0: ──┤ U(1.8587,0,0) ├─────■────────────────────
┌─┴───────────────┴──┐┌─┴─┐┌────────────────┐
q_1: ┤ U(1.7783,-2π,-π/2) ├┤ X ├┤ U(0.60229,0,0) ├
└────────────────────┘└───┘└────────────────┘
Quantum Fourier Transform
[ ]:
def QFT(nb_qubits: int, inverse: bool=False):
qft = QCircuit(nb_qubits)
for j in range(nb_qubits):
qft.add(H(j))
qft.add([CRk(i+1, i, j) for i in range(j+1, nb_qubits)])
qft.add(Barrier())
qft.add([SWAP(i, nb_qubits-1-i) for i in range(nb_qubits // 2)])
if inverse:
return qft.inverse()
return qft
[12]:
print(QFT(3))
print(QFT(3, inverse=True))
┌───┐ ░ ░ ░
q_0: ┤ H ├─■────────■────────░────────────────░───────░──X─
└───┘ │P(π/2) │ ░ ┌───┐ ░ ░ │
q_1: ──────■────────┼────────░─┤ H ├─■────────░───────░──┼─
│P(π/4) ░ └───┘ │P(π/4) ░ ┌───┐ ░ │
q_2: ───────────────■────────░───────■────────░─┤ H ├─░──X─
░ ░ └───┘ ░
░ ░ ░ ┌───┐
q_0: ─X──░───────░─────────────────░──■─────────■────────┤ H ├
│ ░ ░ ┌───┐ ░ │ │P(-π/2) └───┘
q_1: ─┼──░───────░──■────────┤ H ├─░──┼─────────■─────────────
│ ░ ┌───┐ ░ │P(-π/4) └───┘ ░ │P(-π/4)
q_2: ─X──░─┤ H ├─░──■──────────────░──■───────────────────────
░ └───┘ ░ ░
Build successive control U^k gates
[ ]:
def c_Uk(unitary: Gate, n_1: int, n_2: int) -> CustomGate:
matrix = np.zeros((2**(n_1 + n_2), 2**(n_1 + n_2)))
n_1_power = 2**n_1
for k in range(n_1_power):
k_vec = np.zeros((n_1_power, n_1_power))
k_vec[k,k] = 1
matrix += np.kron(k_vec, unitary.power(k).to_matrix())
return CustomGate(matrix, list(range(n_1+n_2)))
[19]:
c_Uk(Ry(0.34,0),2,1)
[19]:
CustomGate(UnitaryMatrix(array([[ 1. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ], [ 0. , 1. , 0. , 0. , 0. , 0. , 0. , 0. ], [ 0. , 0. , 0.98558477, -0.16918235, 0. , 0. , 0. , 0. ], [ 0. , 0. , 0.16918235, 0.98558477, 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 0. , 0.94275467, -0.33348709, 0. , 0. ], [ 0. , 0. , 0. , 0. , 0.33348709, 0.94275467, 0. , 0. ], [ 0. , 0. , 0. , 0. , 0. , 0. , 0.87274451, -0.48817725], [ 0. , 0. , 0. , 0. , 0. , 0. , 0.48817725, 0.87274451]])), [0, 1, 2] )
Building the QPE circuit
[ ]:
def QPE(
eigen_state: npt.NDArray[np.complex128], U: Gate, n_1: int, n_2: int, shots: int
):
first_register = QCircuit(H.range(n_1))
state_circuit = generate_circuit(eigen_state, n_2)
whool_circuit = first_register @ state_circuit
whool_circuit.add(c_Uk(U, n_1, n_2))
whool_circuit.append(QFT(n_1, inverse=True))
# Extract result
whool_circuit.add(BasisMeasure(list(range(n_1)), shots=shots))
result = run(whool_circuit, IBMDevice.AER_SIMULATOR)
probabilities = list(result.probabilities)
print(probabilities)
most_probable = probabilities.index(max(probabilities))
print(most_probable)
# From basis state to phase
max_count_state = format(most_probable, '0{}b'.format(n_1))[
::-1
] # Reverse the bit order
print(max_count_state)
phase_estimation = int(max_count_state, 2) / (2**n_1)
return phase_estimation
[ ]:
state = np.array([0, 1])
U = CustomGate(np.diag([1, -1]), [0])
print(np.linalg.eig(U.to_matrix()))
n_1 = 3
n_2 = 1
phase = QPE(state, U, n_1, n_2, 10000)
print("phase :", phase)
print("eigenvalue", np.exp(1j * 2 * np.pi * phase))
EigResult(eigenvalues=array([ 1., -1.]), eigenvectors=array([[1., 0.],
[0., 1.]]))
[0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0]
4
001
phase : 0.125
eigenvalue (0.7071067811865476+0.7071067811865476j)
[65]:
[65]:
EigResult(eigenvalues=array([ 1., -1.]), eigenvectors=array([[1., 0.],
[0., 1.]]))
[59]:
X(0).to_matrix()
[59]:
array([[0., 1.],
[1., 0.]])