# What's the paper referenced by qiskit.compiler.transpile? My tranpile solution is wrong from the perspective of fidelity

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')


with output as follows:

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')


with output figure:

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
'''


The Qiskit transpilation process does not currently preserve the unitary equivalence, because it is oriented to real hardware that uses measurements.

Here is your circuit with the only addition of measure_all at the end:

from numpy import pi
from qiskit import QuantumCircuit

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.measure_all()
qc.draw('mpl')


When transpiled, the measurements are shuffle.

from qiskit.compiler import transpile

trans_qc = transpile(qc, basis_gates=['cx', 'u3'], coupling_map=[[0, 1], [1, 2]])
trans_qc.draw(output='mpl')


• Thanks for your answer. If Qiskit transpilation process does not preserve unitary equivalence, what equivalence does it preserve? The same output state with the before-transpiled circuit when the initial state is specified as $\ket{0}$? Aug 6 at 23:50
• Hmm.. I would say "observable"equivalence (not an official name). The measurement is moved and allocated in the right place. From the outside observer perspective (as the observer that looks at the result of measuring), there is no difference. Aug 9 at 12:52