You're reading the documentation of the v0.11. For the latest released version, please have a look at v0.12.

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 Knill CNOT) 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^†\]

QPT reconstructs the \(\chi\) matrix.

[46]:
import perceval as pcvl
import numpy as np
import matplotlib.pyplot as plt
from perceval.components import catalog, Processor, BS
from perceval.components.source import Source
from perceval.algorithm import ProcessTomography
from perceval.algorithm.tomography import is_physical, process_fidelity
from perceval.algorithm import ProcessTomographyMLE, StateTomographyMLE

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 (Knill) CNOT gate

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

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

[48]:
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})\]
[49]:
# 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 = process_fidelity(chi_op, chi_op_ideal)
print("Process Fidelity of CNOT gate operation", cnot_fidelity)
Process Fidelity of CNOT gate operation 1.0000000000000018

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^† U^† \varepsilon(E_j))\]
[50]:
f_avg = qpt.average_fidelity(op_CX)
print("average fidelity :",f_avg)
average fidelity : 0.999999999999995

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.

[51]:
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.

[52]:
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 Knill 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 Knill CNOT implemented is = (0.07407407407407397-1.3688516820577952e-17j)

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.

[53]:
# 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["heralded 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_imperfect_source.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)

plt.imshow((abs(U)), norm='logit')
plt.suptitle('Absolute values of Error Process Map')
plt.title('Colors on log scale, white pixels denote values where U=0')
plt.colorbar()
plt.show()

print('The upper left coefficient of the error process map', U[0,0])
../_images/notebooks_Tomography_walkthrough_19_0.png
The upper left coefficient of the error process map (0.9460501201747041+1.2248239266005459e-16j)

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.

Maximum Likelihood Estimation (MLE) for Tomography

An interesting question is to explore techniques to generate completely positive maps. Perceval features an implementations of maximum likelihood estimation for State and Process Tomography.

State Tomography using MLE is demonstrated by creating a processor that generates a GHZ state.

[54]:
GHZ_TARGET = np.zeros((8, 8))
GHZ_TARGET[0, 0], GHZ_TARGET[0, -1], GHZ_TARGET[-1, 0], GHZ_TARGET[-1, -1] = 1, 1, 1, 1
GHZ_TARGET /= 2

# Creating a processor to generate GHZ state
ghz_state_proc = Processor("SLOS", 6)
ghz_state_proc.add(0, BS.H())
ghz_state_proc.add(0, cnot)
ghz_state_proc.add(2, cnot)

# performing state tomography on the processor to reconstruct GHZ state
s_mle = StateTomographyMLE(ghz_state_proc)
ghz_state = s_mle.state_tomography_density_matrix()

fidelity = s_mle.state_fidelity(GHZ_TARGET, ghz_state)

print("Fidelity of the GHZ state reconstruction :", fidelity)
print("Trace of the reconstructed density matrix :", np.trace(ghz_state))
Fidelity of the GHZ state reconstruction : 1.0000000000000005551
Trace of the reconstructed density matrix : (1-3.7982270983039195e-65j)

The Knill CNOT gate, previously used in process tomography, is utilized here to illustrate how the MLE technique reconstructs a physical process map.

Remark: The construction of \(\chi\) matrix here does not include normalization by the gate efficiency which had produced an unphysical map earlier with un-normalised \(\chi\).

[55]:
# Performing process tomography using MLE
qpt_mle = ProcessTomographyMLE(operator_processor=cnot)
chi_cnot_mle = qpt_mle.chi_matrix()

chi_cnot_mle_ideal = qpt_mle.chi_target(op_CX)
cnot_mle_fidelity = process_fidelity(chi_cnot_mle, chi_cnot_mle_ideal)
[56]:
print('The fidelity of process tomography MLE to reconstruct the process map :', cnot_mle_fidelity)
print('Is this process physical', is_physical(chi_cnot_mle, 2))
The fidelity of process tomography MLE to reconstruct the process map : 1.0
Is this process physical {'Trace=1': True, 'Hermitian': True, 'Completely Positive': True}