Tomography of a CNOT Gate

This notebook is directed to explain how a Quantum Process Tomography experiment can be performed in Perceval. 2 qubit Heralded CNOT gate (created using the KLM protocol) is used as the target gate operation under study to demonstrate the experiment and the results.

Quantum Process Tomography

Quantum Process Tomography (QPT) is a tool to reconstruct the mathematical operation associated to a physical gate by a series of different measurements on different inputs. QPT scales exponentially with the number of qubits. Any quantum channel (completely positive quantum operation) has a \(\chi\) matrix representation :

\[\varepsilon(\rho)=\sum_{m,n}\chi_{mn}E_m\rho E_n^{\dag}\]

QPT reconstructs the \(\chi\) matrix.

[1]:
import perceval as pcvl
import numpy as np
from perceval.components import catalog
from perceval.components.source import Source
from perceval.algorithm import ProcessTomography
from perceval.algorithm.tomography import is_physical

In Perceval

ProcessTomography requires a Perceval Processor as input which includes the Source and the Gate or Operation Circuit under study. Note that any source imperfections needs to be included in the Processor. Any heralding and/or any post-selection should also be defined within the Processor (see Processor for more information)

The Heralded (KLM protocol) CNOT gate

[2]:
cnot = catalog["klm cnot"].build_processor()
pcvl.pdisplay(cnot, recursive=True)
[2]:
../_images/notebooks_Tomography_walkthrough_6_0.svg

Instantiating a ProcessTomography object with KLM CNOT gate from Perceval’s catalog with a perfect source and computing the \(\chi\) matrix for the gate operation.

[3]:
qpt = ProcessTomography(operator_processor=cnot)
chi_op = qpt.chi_matrix()  # computing the chi matrix
pcvl.pdisplay(qpt, render_size=(15,30))  # visualization of the same chi
../_images/notebooks_Tomography_walkthrough_8_0.png

Process Fidelity Computations

Once having the χ matrix, it might be useful to know how close the heralded gate is to the real CNOT operation. We can compute the process fidelity

\[F_{\chi}= Tr({\chi}_{ideal} {\chi}_{physical})\]
[4]:
# checking fidelity
op_CX = np.array([[1, 0, 0, 0],
             [0, 1, 0, 0],
             [0, 0, 0, 1],
             [0, 0, 1, 0]], dtype='complex_')

chi_op_ideal = qpt.chi_target(op_CX)
cnot_fidelity = qpt.process_fidelity(chi_op, chi_op_ideal)
print("Process Fidelity of CNOT gate operation", cnot_fidelity)
Process Fidelity of CNOT gate operation 1.000000000000007

As expected, the fidelity is one. The gate behaves as designed in the ideal case.

Average fidelity

Computing the process fidelity can be quite long because we are manipulating matrices that scale exponentially with the number of qubits. If we are only interested in the fidelity of the gate, we can compute its average fidelity :

\[\bar{F}= \frac{1}{d+1}+\frac{1}{d^2(d+1)}\sum_j Tr(U E_j^\dag U^\dag \varepsilon(E_j))\]
[5]:
f_avg = qpt.average_fidelity(op_CX)
print("average fidelity :",f_avg)
average fidelity : 0.9999999999999963

Physicality of the maps

When we reconstruct a process map, an important question is to know whether it is physical or not. A map is called physical if it is trace-preserving (TP), Hermitian, and completely positive (CP).

A map is TP if its \(\chi\) matrix is trace 1. To check the CP part is a bit more difficult: The general algorithm (for finite dimensions is using the Choi-Jamiolkowski-isomorphism.

A map \(\epsilon\) is CP iff the Choi matrix

\[S:=(\varepsilon \otimes I_d)(\ket{\Omega}\bra{\Omega})\]

is a positive semidefinite matrix, where ∣Ω⟩=∑i=1d​∣ii⟩ is the maximally entangled state.

[6]:
print('Results from testing the physicality of the Chi matrix for the CNOT gate operation')
print(is_physical(chi_op, 2))
Results from testing the physicality of the Chi matrix for the CNOT gate operation
{'Trace=1': True, 'Hermitian': True, 'Completely Positive': True}

It must be mentioned that the \(\chi\) computed by tomography is normalized by the gate efficiency to be a trace preserving physical map. The user can, however, ask for the un-normalized \(\chi\) and the gate efficiency if needed.

[7]:
chi_raw = qpt.chi_unnormalized
gate_eff = qpt.gate_efficiency
print("Un normalized Chi matrix is not physical: not Trace preserving")
print(is_physical(chi_raw, 2))
print("Gate efficiency of the KLM CNOT implemented is =", gate_eff)
Un normalized Chi matrix is not physical: not Trace preserving
{'Trace=1': False, 'Hermitian': True, 'Completely Positive': True}
Gate efficiency of the KLM CNOT implemented is = (0.05132078828084498-5.594292530574606e-18j)

Error Process Map

A nice way to highlight the errors in the computation is to look at the error process map.

\[\chi_{err}=V \chi V^†\]
\[V_{mn} = \frac{Tr(E_m^† E_n U^†)}{d}\]

The error map is computed in the following cell by incorporating source imperfections to the CNOT gate processor.

[ ]:
# Error map for an operation with an imperfect source with properties g2=0.00732, indistinguishability=0.9438
src = Source(multiphoton_component=0.00732, indistinguishability=0.9438)
cnot_imperfect_src = catalog["klm cnot"].build_processor()
cnot_imperfect_src.source = src

qpt_imperfect_source = ProcessTomography(nqubit=2, operator_processor=cnot_imperfect_src)
chi_op_imperfect_src = qpt_imperfect_source.chi_matrix()  # computing the chi matrix

U=qpt.error_process_matrix(chi_op_imperfect_src, op_CX)

# many values too small in U, filtering them out
U = np.where(abs(U)<1e-6, 0, U)
print(U)

The upper left coefficient is the process fidelity while all the other non-zero coefficients represent some kind of errors. For example, non-zero imaginary coefficients on the first row (or first column) represent first-order unitary errors. Non-zero diagonal coefficients represent Pauli errors.