Download Notebook

Using Quara’s standard tomography features from QuTiP

Quara supports wrappers for executing standard quantum tomography from QuTiP. Here we briefly explain how to use them with examples on an 1-qubit system and 2 types of estimators.

[1]:
from math import sqrt

from qutip import basis, ket2dm, identity, sigmax, sigmay, to_super
from qutip.qip.operations.gates import hadamard_transform

from quara.interface.qutip.api import (
    estimate_standard_povmt_from_qutip,
    estimate_standard_qpt_from_qutip,
    estimate_standard_qst_from_qutip,
    generate_empi_dists_from_qutip_gate,
    generate_empi_dists_from_qutip_povm,
    generate_empi_dists_from_qutip_state,
)

First, we define quantum objects in QuTiP. We will use these objects as true objects (target objects of estimation) and tester objects (objects that are used to inspect other quantum objects). States can be defined as either ket or oper. POVMs can only be defined as list of oper. Gates can only be defined as super operators.

In this tutorial we will define true objects due to that we don’t have experimental data. Although it is not needed to define true objects if you have empirical distribution of projective measurements from an experiment.

[2]:
# states
x0 = ket2dm((basis(2,0) + basis(2,1)).unit()) # (|0> + |1>)/sqrt(2)
x1 = ket2dm((basis(2,0) - basis(2,1)).unit()) # (|0> - |1>)/sqrt(2)
y0 = ket2dm((basis(2,0) + 1j * basis(2,1)).unit()) # (|0> + i|1>)/sqrt(2)
y1 = ket2dm((basis(2,0) - 1j * basis(2,1)).unit()) # (|0> - i|1>)/sqrt(2)
z0 = ket2dm(basis(2,0)) # |0>
z1 = ket2dm(basis(2,1)) # |1>
a = (identity(2) + sigmax()/sqrt(2) + sigmay()/sqrt(2))/2

# POVMs
povm_x = [x0, x1]
povm_y = [y0, y1]
povm_z = [z0, z1]

# gate
gate_identity = to_super(identity(2))
gate_hadamard = to_super(hadamard_transform(1))

Quantum State Tomography (1-qubit)

Here we consider quantum state tomography of an 1-qubit system. We need to define tester POVMs when we perform quantum state tomography using Quara.

[3]:
# Testers
tester_povms = [povm_x, povm_y, povm_z]

# True object
true_state = a
print(true_state)
Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True
Qobj data =
[[0.5       +0.j         0.35355339-0.35355339j]
 [0.35355339+0.35355339j 0.5       +0.j        ]]

We will genearate empirical distributions from the given true object. If you have your own list of empirical distributions make sure that the type matches to List[Tuple[int, np.ndarray]].

[4]:
# define system
mode = "qubit"
num = 1

# define requirement for empirical distribution
num_data = 1000
seed = 7896

# calculate empirical distribution
empi_dists = generate_empi_dists_from_qutip_state(mode_system=mode, num_system=num, true_state=true_state, tester_povms=tester_povms, num_sum=num_data, seed=seed, schedules="all")
for f in empi_dists:
    print(f)
(1000, array([0.864, 0.136]))
(1000, array([0.844, 0.156]))
(1000, array([0.49, 0.51]))

Now, we execute quantum state tomography using linear estimation. You can estimate the quantum state just by calling estimate_standard_qst_from_qutip(). You can choose linear estimation by specifying estimator_name="linear".

[5]:
# estimate the state using linear estimator
estimate = estimate_standard_qst_from_qutip(mode_system=mode, num_system=num, tester_povms=tester_povms, empi_dists=empi_dists, estimator_name="linear", schedules="all")
print(estimate)
Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True
Qobj data =
[[0.49 +0.j    0.364-0.344j]
 [0.364+0.344j 0.51 +0.j   ]]

You can choose least squares estimation by specifying estimator_name="least_squares".

[6]:
# estimate the state using least squares estimator
estimate = estimate_standard_qst_from_qutip(mode_system=mode, num_system=num, tester_povms=tester_povms, empi_dists=empi_dists, estimator_name="least_squares", schedules="all")
print(estimate)
Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True
Qobj data =
[[0.49001861+0.j         0.36332341-0.34336059j]
 [0.36332341+0.34336059j 0.50998144+0.j        ]]

POVM tomography (1-qubit)

Here we consider POVM tomography of a 1-qubit system. We need to define tester states when we perform POVM tomography using Quara.

[7]:
# Testers
tester_states = [x0, y0, z0, z1]

# True object
true_povm = povm_z
for q_obj in true_povm:
    print(q_obj)
Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True
Qobj data =
[[1. 0.]
 [0. 0.]]
Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True
Qobj data =
[[0. 0.]
 [0. 1.]]
[8]:
# define system
mode = "qubit"
num = 1

# define requirement for empirical distribution
num_data = 1000
seed = 7896

# calculate empirical distribution
empi_dists = generate_empi_dists_from_qutip_povm(mode_system=mode, num_system=num, true_povm=true_povm, tester_states=tester_states, num_sum=num_data, seed=seed, schedules="all")
for f in empi_dists:
    print(f)
(1000, array([0.496, 0.504]))
(1000, array([0.502, 0.498]))
(1000, array([1., 0.]))
(1000, array([0., 1.]))

When performing POVM tomography make sure you specify num_outcomes.

[9]:
# estimate POVM using linear estimator
estimate = estimate_standard_povmt_from_qutip(mode_system=mode, num_system=num, num_outcomes=2, tester_states=tester_states, empi_dists=empi_dists, estimator_name="linear", schedules="all")
for q_obj in estimate:
    print(q_obj)
Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True
Qobj data =
[[ 1.00000000e+00+0.j    -4.00000000e-03-0.002j]
 [-4.00000000e-03+0.002j -1.11022302e-16+0.j   ]]
Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True
Qobj data =
[[1.11022302e-16+0.j    4.00000000e-03+0.002j]
 [4.00000000e-03-0.002j 1.00000000e+00+0.j   ]]
[10]:
# estimate POVM using least squares estimator
estimate = estimate_standard_povmt_from_qutip(mode_system=mode, num_system=num, num_outcomes=2, tester_states=tester_states, empi_dists=empi_dists, estimator_name="least_squares", schedules="all")
for q_obj in estimate:
    print(q_obj)
Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True
Qobj data =
[[ 9.99980029e-01+0.j         -3.99968437e-03-0.00199982j]
 [-3.99968437e-03+0.00199982j  1.99971568e-05+0.j        ]]
Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True
Qobj data =
[[1.99712481e-05+0.j         3.99968437e-03+0.00199982j]
 [3.99968437e-03-0.00199982j 9.99980003e-01+0.j        ]]

Quantum Process Tomography (1-qubit)

Finally, we consider performing quantum process tomography of a 1-qubit system from QuTiP.

[11]:
# Testers
tester_states = [x0, y0, z0, z1]
tester_povms = [povm_x, povm_y, povm_z]

# True object
true_gate = gate_hadamard
print(true_gate)
Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = (4, 4), type = super, isherm = True
Qobj data =
[[ 0.5  0.5  0.5  0.5]
 [ 0.5 -0.5  0.5 -0.5]
 [ 0.5  0.5 -0.5 -0.5]
 [ 0.5 -0.5 -0.5  0.5]]
[12]:
# define system
mode = "qubit"
num = 1

# define requirement for empirical distribution
num_data = 1000
seed = 7896

# calculate empirical distribution
empi_dists = generate_empi_dists_from_qutip_gate(mode_system=mode, num_system=num, true_gate=true_gate, tester_states=tester_states, tester_povms=tester_povms, num_sum=num_data, seed=seed, schedules="all")
for f in empi_dists:
    print(f)
(1000, array([0.496, 0.504]))
(1000, array([0.502, 0.498]))
(1000, array([1., 0.]))
(1000, array([0.488, 0.512]))
(1000, array([0., 1.]))
(1000, array([0.491, 0.509]))
(1000, array([1., 0.]))
(1000, array([0.507, 0.493]))
(1000, array([0.513, 0.487]))
(1000, array([0., 1.]))
(1000, array([0.47, 0.53]))
(1000, array([0.497, 0.503]))
[13]:
# estimate the gate using linear estimator
estimate = estimate_standard_qpt_from_qutip(mode_system=mode, num_system=num, tester_states=tester_states, tester_povms=tester_povms, empi_dists=empi_dists, estimator_name="linear", schedules="all")
print(estimate)
Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = (4, 4), type = super, isherm = False
Qobj data =
[[ 0.513 +0.j      0.495 +0.014j   0.495 -0.014j   0.497 +0.j    ]
 [ 0.5   +0.007j  -0.4925+0.0255j  0.4845+0.0015j -0.5   -0.03j  ]
 [ 0.5   -0.007j   0.4845-0.0015j -0.4925-0.0255j -0.5   +0.03j  ]
 [ 0.487 +0.j     -0.495 -0.014j  -0.495 +0.014j   0.503 +0.j    ]]
[14]:
# estimate the gate using least squares estimator
estimate = estimate_standard_qpt_from_qutip(mode_system=mode, num_system=num, tester_states=tester_states, tester_povms=tester_povms, empi_dists=empi_dists, estimator_name="least_squares", schedules="all")
print(estimate)
Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = (4, 4), type = super, isherm = False
Qobj data =
[[ 0.51438824+0.j          0.48853761+0.00725424j  0.48853761-0.00725424j
   0.49664456+0.j        ]
 [ 0.49329968+0.00741707j -0.49249075-0.00293811j  0.48871849+0.0007454j
  -0.49336835-0.01536569j]
 [ 0.49329968-0.00741707j  0.48871849-0.0007454j  -0.49249075+0.00293811j
  -0.49336835+0.01536569j]
 [ 0.48561176+0.j         -0.48853761-0.00725424j -0.48853761+0.00725424j
   0.50335544+0.j        ]]
[ ]:

Download Notebook