Here I want to transpile a standard 3 qubit Quantum Fourier Transform circuit into a implementable circuit. The implementable 3 qubit circuit (qubit0, qubit1, qubit2) should have no SWAP gate and there is no connectivity between qubit0 and qubit2.
from qiskit import QuantumCircuit
from qiskit.compiler import transpile
import numpy as np
from numpy import pi
from qiskit import QuantumCircuit
from qiskit import Aer, transpile
from qiskit.tools.visualization import plot_histogram, plot_state_city
from qiskit.providers.aer.library import save_unitary
import qiskit.quantum_info as qi
qc = QuantumCircuit(3)
qc.h(2)
qc.cp(pi/2, 1, 2)
qc.cp(pi/4, 0, 2)
qc.h(1)
qc.cp(pi/2, 0, 1)
qc.h(0)
qc.swap(0,2)
qc.draw(output='mpl', filename=r'./QFT_figure/circuit_1.png')
The above code has an output png file as follows:
And the corresponding unitary matrix is the 3 qubit QFT matrix:
# from numpy import pi
w = np.cos(np.pi/4) + 1j * np.sin(np.pi/4)
QFT_matrix = 1/np.sqrt(8)*np.array([[1, 1, 1, 1, 1, 1, 1, 1],
[1, w, w**2, w**3, w**4, w**5, w**6, w**7],
[1, w**2, w**4, w**6, 1, w**2, w**4, w**6],
[1, w**3, w**6, w, w**4, w**7, w**2, w**5],
[1, w**4, 1, w**4, 1, w**4, 1, w**4],
[1, w**5, w**2, w**7, w**4, w, w**6, w**3],
[1, w**6, w**4, w**2, 1, w**6, w**4, w**2],
[1, w**7, w**6, w**5, w**4, w**3, w**2, w]], dtype=np.complex128)
# Construct quantum circuit without measure
qc = QuantumCircuit(3)
qc.h(2)
qc.cp(np.pi/2, 1, 2)
qc.cp(np.pi/4, 0, 2)
qc.h(1)
qc.cp(np.pi/2, 0, 1) # CROT from qubit 0 to qubit 1
qc.h(0)
qc.swap(0,2)
qc.save_unitary()
# Transpile for simulator
simulator = Aer.get_backend('aer_simulator')
qc = transpile(qc, simulator)
# Run and get unitary
result = simulator.run(qc).result()
unitary = result.get_unitary(qc)
print(unitary)
print("Circuit unitary:\n", np.allclose(unitary.round(5), QFT_matrix))
print('=========================================')
'''
Operator([[ 3.53553391e-01+0.00000000e+00j,
3.53553391e-01-4.32978028e-17j,
3.53553391e-01-4.32978028e-17j,
3.53553391e-01-8.65956056e-17j,
3.53553391e-01-4.32978028e-17j,
3.53553391e-01-8.65956056e-17j,
3.53553391e-01-8.65956056e-17j,
3.53553391e-01-1.29893408e-16j],
[ 3.53553391e-01+0.00000000e+00j,
2.50000000e-01+2.50000000e-01j,
6.49467042e-17+3.53553391e-01j,
-2.50000000e-01+2.50000000e-01j,
-3.53553391e-01+4.32978028e-17j,
-2.50000000e-01-2.50000000e-01j,
-1.08244507e-16-3.53553391e-01j,
2.50000000e-01-2.50000000e-01j],
[ 3.53553391e-01+0.00000000e+00j,
6.49467042e-17+3.53553391e-01j,
-3.53553391e-01+4.32978028e-17j,
-1.08244507e-16-3.53553391e-01j,
3.53553391e-01-4.32978028e-17j,
1.08244507e-16+3.53553391e-01j,
-3.53553391e-01+8.65956056e-17j,
-1.51542310e-16-3.53553391e-01j],
[ 3.53553391e-01+0.00000000e+00j,
-2.50000000e-01+2.50000000e-01j,
-6.49467042e-17-3.53553391e-01j,
2.50000000e-01+2.50000000e-01j,
-3.53553391e-01+4.32978028e-17j,
2.50000000e-01-2.50000000e-01j,
1.08244507e-16+3.53553391e-01j,
-2.50000000e-01-2.50000000e-01j],
[ 3.53553391e-01+0.00000000e+00j,
-3.53553391e-01+4.32978028e-17j,
3.53553391e-01-4.32978028e-17j,
-3.53553391e-01+8.65956056e-17j,
3.53553391e-01-4.32978028e-17j,
-3.53553391e-01+8.65956056e-17j,
3.53553391e-01-8.65956056e-17j,
-3.53553391e-01+1.29893408e-16j],
[ 3.53553391e-01+0.00000000e+00j,
-2.50000000e-01-2.50000000e-01j,
6.49467042e-17+3.53553391e-01j,
2.50000000e-01-2.50000000e-01j,
-3.53553391e-01+4.32978028e-17j,
2.50000000e-01+2.50000000e-01j,
-1.08244507e-16-3.53553391e-01j,
-2.50000000e-01+2.50000000e-01j],
[ 3.53553391e-01+0.00000000e+00j,
-6.49467042e-17-3.53553391e-01j,
-3.53553391e-01+4.32978028e-17j,
1.08244507e-16+3.53553391e-01j,
3.53553391e-01-4.32978028e-17j,
-1.08244507e-16-3.53553391e-01j,
-3.53553391e-01+8.65956056e-17j,
1.51542310e-16+3.53553391e-01j],
[ 3.53553391e-01+0.00000000e+00j,
2.50000000e-01-2.50000000e-01j,
-6.49467042e-17-3.53553391e-01j,
-2.50000000e-01-2.50000000e-01j,
-3.53553391e-01+4.32978028e-17j,
-2.50000000e-01+2.50000000e-01j,
1.08244507e-16+3.53553391e-01j,
2.50000000e-01+2.50000000e-01j]],
input_dims=(2, 2, 2), output_dims=(2, 2, 2))
Circuit unitary:
True
=========================================
'''
I exploit the transpile function to implement compiling:
trans_qc = transpile(qc, basis_gates=['cz', 'u3'])
trans_qc.draw(output='mpl', filename=r'./QFT_figure/circuit_2.png')
Now I want to print the unitary corresponding to circuit_2, which is identical to QFT matrix
from numpy import pi
# Construct quantum circuit without measure
qubits = [0, 1, 2]
# qubits = [2, 1, 0]
qc = QuantumCircuit(3)
qc.u(0, 0, pi/8, qubits[0])
qc.u(0, 0, pi/4, qubits[1])
qc.u(0, 0.962, -0.962, qubits[2])
qc.cz(qubits[1], qubits[2])
qc.u(pi/4, pi/2, -pi/2, qubits[2])
qc.cz(qubits[1], qubits[2])
qc.u(0, 0.962, -0.962, qubits[1])
qc.u(pi/4, -pi/2, pi/2, qubits[2])
qc.cz(qubits[0], qubits[2])
qc.u(pi/8, pi/2, -pi/2, qubits[2])
qc.cz(qubits[0], qubits[2])
qc.u(0, 0, pi/4, qubits[0])
qc.u(pi/8, -pi/2, pi/2, qubits[2])
qc.cz(qubits[0], qubits[1])
qc.u(pi/4, pi/2, -pi/2, qubits[1])
qc.cz(qubits[0], qubits[1])
qc.u(pi/2, 0, pi, qubits[0])
qc.u(pi/2, pi/4, -pi, qubits[1])
qc.cz(qubits[0], qubits[2])
qc.u(pi/2, 0, pi, qubits[0])
qc.u(pi/2, 0, pi, qubits[2])
qc.cz(qubits[0], qubits[2])
qc.u(pi/2, 0, pi, qubits[0])
qc.u(pi/2, 0, pi, qubits[2])
qc.cz(qubits[0], qubits[2])
qc.u(pi/2, 0, pi, qubits[2])
qc.draw(output='mpl', filename=r'.\circuit_2.png')
qc.save_unitary()
# Transpile for simulator
simulator = Aer.get_backend('aer_simulator')
qc = transpile(qc, simulator)
# Run and get unitary
result = simulator.run(qc).result()
unitary_2 = result.get_unitary(qc)
print("Circuit unitary:\n", unitary_2.round(5))
print("Circuit unitary:\n", np.allclose(unitary.round(5), QFT_matrix))
print('=========================================')
'''
Circuit unitary:
[[ 0.32664+0.1353j 0.32664+0.1353j 0.32664+0.1353j 0.32664+0.1353j
0.32664+0.1353j 0.32664+0.1353j 0.32664+0.1353j 0.32664+0.1353j ]
[ 0.32664+0.1353j 0.1353 +0.32664j -0.1353 +0.32664j -0.32664+0.1353j
-0.32664-0.1353j -0.1353 -0.32664j 0.1353 -0.32664j 0.32664-0.1353j ]
[ 0.32664+0.1353j -0.1353 +0.32664j -0.32664-0.1353j 0.1353 -0.32664j
0.32664+0.1353j -0.1353 +0.32664j -0.32664-0.1353j 0.1353 -0.32664j]
[ 0.32664+0.1353j -0.32664+0.1353j 0.1353 -0.32664j 0.1353 +0.32664j
-0.32664-0.1353j 0.32664-0.1353j -0.1353 +0.32664j -0.1353 -0.32664j]
[ 0.32664+0.1353j -0.32664-0.1353j 0.32664+0.1353j -0.32664-0.1353j
0.32664+0.1353j -0.32664-0.1353j 0.32664+0.1353j -0.32664-0.1353j ]
[ 0.32664+0.1353j -0.1353 -0.32664j -0.1353 +0.32664j 0.32664-0.1353j
-0.32664-0.1353j 0.1353 +0.32664j 0.1353 -0.32664j -0.32664+0.1353j ]
[ 0.32664+0.1353j 0.1353 -0.32664j -0.32664-0.1353j -0.1353 +0.32664j
0.32664+0.1353j 0.1353 -0.32664j -0.32664-0.1353j -0.1353 +0.32664j]
[ 0.32664+0.1353j 0.32664-0.1353j 0.1353 -0.32664j -0.1353 -0.32664j
-0.32664-0.1353j -0.32664+0.1353j -0.1353 +0.32664j 0.1353 +0.32664j]]
Circuit unitary:
True
=========================================
'''
Considering the constrain of the connectivity between qubits, I exploit the hyperparameter coupling_map and change the basis set from cz to cx:
trans_qc_2 = transpile(qc, basis_gates=['cx', 'u3'], coupling_map=[[0, 1], [1, 2]])
trans_qc_2.draw(output='mpl', filename=r'./QFT_figure/circuit_2_coupling.png')
Its corresponding unitary matrix is not good: from
numpy import pi
# Construct quantum circuit without measure
qubits = [0, 1, 2]
# qubits = [2, 1, 0]
qc = QuantumCircuit(3)
qc.u(pi/2, 0, -3*pi/4, qubits[1])
qc.u(pi/2, 0, -7*pi/8, qubits[2])
qc.cnot(qubits[0], qubits[1])
qc.u(pi/4, pi/2, -pi/2, qubits[0])
qc.u(0, -pi, -pi, qubits[1])
qc.cnot(qubits[0], qubits[1])
qc.u(pi/2, pi/4, -pi, qubits[0])
qc.u(pi/2, 0, -pi, qubits[1])
qc.cnot(qubits[0], qubits[1])
qc.u(pi/2, 0, -pi, qubits[0])
qc.u(pi/2, 0, -pi, qubits[1])
qc.cnot(qubits[0], qubits[1])
qc.u(pi/2, 0, -pi, qubits[0])
qc.u(pi/2, 0, -pi, qubits[1])
qc.cnot(qubits[0], qubits[1])
qc.u(pi/2, 0, pi, qubits[0])
qc.u(pi/2, 0, -pi, qubits[1])
qc.cnot(qubits[1], qubits[2])
qc.u(pi/8, pi/2, -pi/2, qubits[1])
qc.u(0, -pi, -pi, qubits[2])
qc.cnot(qubits[1], qubits[2])
qc.u(pi/2, pi/8, -pi, qubits[1])
qc.u(pi/4, -pi/2, pi/2, qubits[2])
qc.cnot(qubits[0], qubits[1])
qc.u(pi/2, 0, -pi, qubits[0])
qc.u(pi/2, 0, -pi, qubits[1])
qc.cnot(qubits[0], qubits[1])
qc.u(pi/2, 0, -pi, qubits[0])
qc.u(pi/2, 0, -pi, qubits[1])
qc.cnot(qubits[0], qubits[1])
qc.u(pi/2, 0, -pi, qubits[1])
qc.cnot(qubits[1], qubits[2])
qc.u(pi/4, pi/2, -pi/2, qubits[1])
qc.u(0, -pi, -pi, qubits[2])
qc.cnot(qubits[1], qubits[2])
qc.u(pi/2, pi/4, -pi, qubits[1])
qc.u(pi/2, 0, -pi, qubits[2])
qc.cnot(qubits[0], qubits[1])
qc.u(pi/2, 0, -pi, qubits[0])
qc.u(pi/2, 0, -pi, qubits[1])
qc.cnot(qubits[0], qubits[1])
qc.u(pi/2, 0, -pi, qubits[0])
qc.u(pi/2, 0, -pi, qubits[1])
qc.cnot(qubits[0], qubits[1])
qc.u(pi/2, 0, -pi, qubits[1])
qc.cnot(qubits[1], qubits[2])
qc.u(pi/2, 0, -pi, qubits[1])
qc.u(pi/2, 0, -pi, qubits[2])
qc.cnot(qubits[1], qubits[2])
qc.u(pi/2, 0, -pi, qubits[1])
qc.u(pi/2, 0, -pi, qubits[2])
qc.cnot(qubits[1], qubits[2])
qc.u(pi/2, 0, -pi, qubits[1])
qc.u(pi/2, 0, -pi, qubits[2])
qc.draw(output='mpl', filename=r'.\circuit_5.png')
qc.save_unitary()
# Transpile for simulator
simulator = Aer.get_backend('aer_simulator')
qc = transpile(qc, simulator)
# Run and get unitary
result = simulator.run(qc).result()
unitary_5 = result.get_unitary(qc)
print("Circuit unitary:\n", unitary_5.round(5))
print("Circuit unitary:\n", np.allclose(unitary.round(5), QFT_matrix))
print('=========================================')
'''
Circuit unitary:
[[ 0.29397+0.19642j 0.29397+0.19642j 0.29397+0.19642j 0.29397+0.19642j
0.29397+0.19642j 0.29397+0.19642j 0.29397+0.19642j 0.29397+0.19642j]
[ 0.29397+0.19642j 0.29397+0.19642j -0.29397-0.19642j -0.29397-0.19642j
-0.19642+0.29397j -0.19642+0.29397j 0.19642-0.29397j 0.19642-0.29397j]
[ 0.29397+0.19642j 0.29397+0.19642j 0.29397+0.19642j 0.29397+0.19642j
-0.29397-0.19642j -0.29397-0.19642j -0.29397-0.19642j -0.29397-0.19642j]
[ 0.29397+0.19642j 0.29397+0.19642j -0.29397-0.19642j -0.29397-0.19642j
0.19642-0.29397j 0.19642-0.29397j -0.19642+0.29397j -0.19642+0.29397j]
[ 0.29397+0.19642j -0.29397-0.19642j -0.19642+0.29397j 0.19642-0.29397j
0.06897+0.34676j -0.06897-0.34676j -0.34676+0.06897j 0.34676-0.06897j]
[ 0.29397+0.19642j -0.29397-0.19642j 0.19642-0.29397j -0.19642+0.29397j
-0.34676+0.06897j 0.34676-0.06897j 0.06897+0.34676j -0.06897-0.34676j]
[ 0.29397+0.19642j -0.29397-0.19642j -0.19642+0.29397j 0.19642-0.29397j
-0.06897-0.34676j 0.06897+0.34676j 0.34676-0.06897j -0.34676+0.06897j]
[ 0.29397+0.19642j -0.29397-0.19642j 0.19642-0.29397j -0.19642+0.29397j
0.34676-0.06897j -0.34676+0.06897j -0.06897-0.34676j 0.06897+0.34676j]]
Circuit unitary:
True
=========================================
'''
Now I calculate the fidelity between unitary and unitary_2, the fidelity between unitary and unitary_5.
def qpt_fidelity_0(chi, chi_id):
return np.abs(np.trace(chi @ chi_id.T.conj())) / np.sqrt(
np.trace(chi @ chi.T.conj()) * np.trace(chi_id @ chi_id.T.conj())).real
print(qpt_fidelity_0(unitary, unitary_2))
print(qpt_fidelity_0(unitary, unitary_5))
'''
1.0
0.21609151032806828
'''