Download Notebook

Using Quara’s standard tomography features from Forest (2-qubit)

Quara supports object conversions for executing standard quantum tomography from Forest SDK. Here we briefly explain how to perform quantum tomography by using Quara and measurements obtained from quantum virtual machine of Forest SDK.

[1]:
from itertools import product

# quara
from quara.interface.forest.api import (
    generate_pauli_strings_from_povm_name,
    calc_empi_dist_from_observables
)
from quara.objects.composite_system_typical import generate_composite_system
from quara.objects.povm_typical import generate_povm_from_name
from quara.protocol.qtomography.standard.standard_qst import StandardQst
from quara.protocol.qtomography.standard.linear_estimator import LinearEstimator

# pyquil
from pyquil import get_qc, Program
from pyquil.gates import H, CNOT
from pyquil.paulis import PauliTerm
from pyquil.experiment import (
    Experiment,
    ExperimentSetting,
    zeros_state,
)
from pyquil.operator_estimation import measure_observables

First, we define a program for state preparation for tomographic experiment. Then, we create a function obtain_expectations_for_qst() which is a function that performs measuments based on given pauli operators. Pauli operators are operators that can be performed as measurements in Forest SDK. More details will be explained in the next cell. Make sure you have started quilc and qvm on your other consoles. See this page for more information.

[2]:
# example for 2 qubits system
qc = get_qc("2q-qvm")
qubits = [0, 1]

# define initialization of the quantum system
# creating a state called "bell phi plus state"
num_shots = 10000
p = Program()
p += H(qubits[0])
p += CNOT(qubits[0], qubits[1])
p.wrap_in_numshots_loop(num_shots)

def obtain_expectations_for_qst(qc, program, pauli_strings):
    settings = []
    for pauli_str in pauli_strings:
        out_operator = PauliTerm.from_list(list(zip(pauli_str, qubits)))
        settings.append(ExperimentSetting(zeros_state(qubits), out_operator))
    tomo_experiment = Experiment(settings, program)
    expectations = []
    for pauli_str, res in zip(
        pauli_strings,
        measure_observables(
            qc,
            tomo_experiment,
        ),
    ):
        if res.raw_expectation is None:
            # This is the result for II...I operator
            expectations.append(1.0)
        else:
            expectations.append(res.raw_expectation)
        print(f"Raw expectation of {pauli_str}: {expectations[-1]}")
    return expectations

Next, we define a composite system and POVM objects which will make a tomographically complete measuring experiment. Then we calculate a set of desired pauli mesruments by using a pre-difned function named generate_pauli_strings_from_povm_name() in Quara for each POVM. In this example, we create a dictionary for POVM objects and Pauli operator names which both are indexed with povm_name.

[3]:
c_sys = generate_composite_system("qubit", 2)
povms = {}
pauli_strings = {}
povm_names = ["_".join(i) for i in product("xyz", repeat=2)]
for povm_name in povm_names:
    povms[povm_name] = generate_povm_from_name(povm_name, c_sys)
    pauli_strings[povm_name] = generate_pauli_strings_from_povm_name(povm_name)
print(f"Sample of Quara tester object for povm_name: {povm_names[0]}")
print(povms[povm_names[0]])
print("\nPauli strings that will be used in this tomographical experiment:")
pauli_strings
Sample of Quara tester object for povm_name: x_x
Type:
Povm

Dim:
4

Number of outcomes:
4

Vecs:
[[ 0.5  0.5  0.   0.   0.5  0.5  0.   0.   0.   0.   0.   0.   0.   0.
   0.   0. ]
 [ 0.5 -0.5  0.   0.   0.5 -0.5  0.   0.   0.   0.   0.   0.   0.   0.
   0.   0. ]
 [ 0.5  0.5  0.   0.  -0.5 -0.5  0.   0.   0.   0.   0.   0.   0.   0.
   0.   0. ]
 [ 0.5 -0.5  0.   0.  -0.5  0.5  0.   0.   0.   0.   0.   0.   0.   0.
   0.   0. ]]

Pauli strings that will be used in this tomographical experiment:
[3]:
{'x_x': ['XX', 'XI', 'IX', 'II'],
 'x_y': ['XY', 'XI', 'IY', 'II'],
 'x_z': ['XZ', 'XI', 'IZ', 'II'],
 'y_x': ['YX', 'YI', 'IX', 'II'],
 'y_y': ['YY', 'YI', 'IY', 'II'],
 'y_z': ['YZ', 'YI', 'IZ', 'II'],
 'z_x': ['ZX', 'ZI', 'IX', 'II'],
 'z_y': ['ZY', 'ZI', 'IY', 'II'],
 'z_z': ['ZZ', 'ZI', 'IZ', 'II']}

Now we perform projective measurements based on Pauli operators and obtain_expectations_for_qst() which we defined in previous cells. Observed expectations are also stored as dictionary and indexed with povm_name.

[4]:
observables = {}
for povm_name in povm_names:
    observables[povm_name] = obtain_expectations_for_qst(qc, p, generate_pauli_strings_from_povm_name(povm_name))

print(f'Expectations for {povm_names[0]} POVM: {observables[povm_names[0]]}')
Raw expectation of XX: 1.0
Raw expectation of XI: -0.0122
Raw expectation of IX: -0.0052
Raw expectation of II: 1.0
Raw expectation of XY: 0.0194
Raw expectation of XI: 0.0054
Raw expectation of IY: 0.0002
Raw expectation of II: 1.0
Raw expectation of XZ: 0.0218
Raw expectation of XI: -0.0052
Raw expectation of IZ: -0.0022
Raw expectation of II: 1.0
Raw expectation of YX: 0.0092
Raw expectation of YI: -0.016
Raw expectation of IX: -0.0028
Raw expectation of II: 1.0
Raw expectation of YY: -1.0
Raw expectation of YI: 0.0172
Raw expectation of IY: 0.0026
Raw expectation of II: 1.0
Raw expectation of YZ: -0.003
Raw expectation of YI: 0.0068
Raw expectation of IZ: 0.0008
Raw expectation of II: 1.0
Raw expectation of ZX: 0.0044
Raw expectation of ZI: 0.0104
Raw expectation of IX: -0.0028
Raw expectation of II: 1.0
Raw expectation of ZY: 0.0008
Raw expectation of ZI: 0.0086
Raw expectation of IY: 0.0032
Raw expectation of II: 1.0
Raw expectation of ZZ: 1.0
Raw expectation of ZI: 0.0016
Raw expectation of IZ: 0.0048
Raw expectation of II: 1.0
Expectations for x_x POVM: [1.0, -0.0122, -0.0052, 1.0]

We need empirical distributions that correspond to selected POVMs in order to perform QST. In ideal case of projective measurements, probability distribution of a POVM can be calculated from a set of Pauli observables and coefficient matrix \(A_{\mathrm{coefficient}}\).

\[\begin{split}\begin{pmatrix} p_1\\ p_2 \\ \vdots \\ p_{N}\end{pmatrix}= A_{\mathrm{coefficient}} \begin{pmatrix} \langle XX \cdots X \rangle \\ \langle XX \cdots I \rangle \\ \vdots \\ \langle II \cdots I \rangle \end{pmatrix}\end{split}\]

Here we create a set of empirical distributions empi_dists, which are pairs of a repetition number and relative frequencies. We calculate empi_dists from measured expectations by using calc_empi_dist_from_observables() function.

[5]:
empi_dists = []
for povm_name in povm_names:
    empi_dist = calc_empi_dist_from_observables(observables[povm_name], num_shots, pauli_strings[povm_name], povms[povm_name])
    empi_dists.append(empi_dist)
    print(f"{povm_name}: {empi_dist}")

x_x: (10000, array([ 0.49565, -0.00175,  0.00175,  0.50435]))
x_y: (10000, array([0.25625, 0.24645, 0.24385, 0.25345]))
x_z: (10000, array([0.2536, 0.2438, 0.2453, 0.2573]))
y_x: (10000, array([0.2476, 0.2444, 0.251 , 0.257 ]))
y_y: (10000, array([ 0.00495,  0.50365,  0.49635, -0.00495]))
y_z: (10000, array([0.25115, 0.25225, 0.24925, 0.24735]))
z_x: (10000, array([0.253 , 0.2522, 0.2456, 0.2492]))
z_y: (10000, array([0.25315, 0.25115, 0.24845, 0.24725]))
z_z: (10000, array([ 0.5016, -0.0008,  0.0008,  0.4984]))

We generate a StandardQst object. From now on, the flow of QST will be the same as explained in tutorial_for_standardqtomography.

[6]:
tester_povms = []
for povm_name in povm_names:
    tester_povms.append(povms[povm_name])

qst = StandardQst(tester_povms, on_para_eq_constraint=True, schedules="all")

When we choose a linear estimator, the estimate is calculated as follows

[7]:
estimator = LinearEstimator()
result = estimator.calc_estimate(qtomography=qst, empi_dists=empi_dists, is_computation_time_required=True)
estimate = result.estimated_qoperation
print(estimate)
print("is estimate physical? : ", estimate.is_physical())
print("\nEigenvalues are: ", estimate.calc_eigenvalues())
Type:
State

Dim:
4

Vec:
[ 5.00000000e-01 -1.80000000e-03  1.00000000e-03  5.66666667e-04
 -2.00000000e-03  5.00000000e-01  9.70000000e-03  1.09000000e-02
  1.33333333e-03  4.60000000e-03 -5.00000000e-01 -1.50000000e-03
  3.43333333e-03  2.20000000e-03  4.00000000e-04  5.00000000e-01]
is estimate physical? :  False

Eigenvalues are:  [1.0000779004210014, 0.007635233277463086, -0.0007271297383294317, -0.006986003960135253]

An eigenvalue of the estimated density matrix is negative, which violates the requirement of positive-semidefiniteness on density matrix. This kind of violation can occur when we choose a linear estimator. In order to avoid the problem, we need to perform a constraint optimization at the data-processing.

Download Notebook