Note

This is the documentation for the current state of the development branch of Qiskit Experiments. The documentation or APIs here can change prior to being released.

Quantum State Tomography

Quantum tomography is an experimental procedure to reconstruct a description of part of a quantum system from the measurement outcomes of a specific set of experiments. In particular, quantum state tomography reconstructs the density matrix of a quantum state by preparing the state many times and measuring them in a tomographically complete basis of measurement operators.

Note

This tutorial requires the qiskit-aer and qiskit-ibm-runtime packages to run simulations. You can install them with python -m pip install qiskit-aer qiskit-ibm-runtime.

We first initialize a simulator to run the experiments on.

from qiskit_aer import AerSimulator
from qiskit_ibm_runtime.fake_provider import FakePerth

backend = AerSimulator.from_backend(FakePerth())

To run a state tomography experiment, we initialize the experiment with a circuit to prepare the state to be measured. We can also pass in an Operator or a Statevector to describe the preparation circuit.

import qiskit
from qiskit_experiments.framework import ParallelExperiment
from qiskit_experiments.library import StateTomography

# GHZ State preparation circuit
nq = 2
qc_ghz = qiskit.QuantumCircuit(nq)
qc_ghz.h(0)
qc_ghz.s(0)
for i in range(1, nq):
    qc_ghz.cx(0, i)

# QST Experiment
qstexp1 = StateTomography(qc_ghz)
qstdata1 = qstexp1.run(backend, seed_simulation=100).block_for_results()

# Print results
display(qstdata1.analysis_results(dataframe=True))
name experiment components value quality backend run_time trace eigvals raw_eigvals rescaled_psd fitter_metadata conditional_probability positive
cc6c49f4 state StateTomography [Q0, Q1] DensityMatrix([[ 0.47477214+0.00000000e+00j, -... None aer_simulator_from(fake_perth) None 1.0 [0.9144428920979236, 0.04393987897499399, 0.02... [0.9144428920979236, 0.04393987897499399, 0.02... False {'fitter': 'linear_inversion', 'fitter_time': ... 1.0 True
6b2ca792 state_fidelity StateTomography [Q0, Q1] 0.914062 None aer_simulator_from(fake_perth) None None None None None None None None
b111b67b positive StateTomography [Q0, Q1] True None aer_simulator_from(fake_perth) None None None None None None None None

Tomography Results

The main result for tomography is the fitted state, which is stored as a DensityMatrix object:

state_result = qstdata1.analysis_results("state", dataframe=True).iloc[0]
print(state_result.value)
DensityMatrix([[ 0.47477214+0.00000000e+00j, -0.00406901-1.62760417e-04j,
                -0.0139974 +1.62760417e-03j, -0.01123047-4.43359375e-01j],
               [-0.00406901+1.62760417e-04j,  0.03238932+0.00000000e+00j,
                 0.00830078-9.76562500e-04j, -0.00227865-1.30208333e-03j],
               [-0.0139974 -1.62760417e-03j,  0.00830078+9.76562500e-04j,
                 0.02620443+0.00000000e+00j, -0.00992839+1.79036458e-03j],
               [-0.01123047+4.43359375e-01j, -0.00227865+1.30208333e-03j,
                -0.00992839-1.79036458e-03j,  0.46663411+0.00000000e+00j]],
              dims=(2, 2))

We can also visualize the density matrix:

from qiskit.visualization import plot_state_city
state = qstdata1.analysis_results("state", dataframe=True).iloc[0].value
plot_state_city(state, title='Density Matrix')
../../_images/state_tomography_3_0.png

The state fidelity of the fitted state with the ideal state prepared by the input circuit is stored in the "state_fidelity" result field. Note that if the input circuit contained any measurements the ideal state cannot be automatically generated and this field will be set to None.

fid_result = qstdata1.analysis_results("state_fidelity", dataframe=True).iloc[0]
print("State Fidelity = {:.5f}".format(fid_result.value))
State Fidelity = 0.91406

Additional state metadata

Additional data is stored in the tomography under additional fields. This includes

  • eigvals: the eigenvalues of the fitted state

  • trace: the trace of the fitted state

  • positive: Whether the eigenvalues are all non-negative

If trace rescaling was performed this dictionary will also contain a raw_trace field containing the trace before rescaling. Futhermore, if the state was rescaled to be positive or trace 1 an additional field raw_eigvals will contain the state eigenvalues before rescaling was performed.

for col in ["eigvals", "trace", "positive"]:
    print(f"{col}: {state_result[col]}")
eigvals: [0.91444289 0.04393988 0.02646046 0.01515677]
trace: 1.0000000000000016
positive: True

To see the effect of rescaling, we can perform a “bad” fit with very low counts:

# QST Experiment
bad_data = qstexp1.run(backend, shots=10, seed_simulation=100).block_for_results()
bad_state_result = bad_data.analysis_results("state", dataframe=True).iloc[0]

# Print result
for key, val in bad_state_result.items():
    print(f"{key}: {val}")
name: state
experiment: StateTomography
components: [<Qubit(Q0)>, <Qubit(Q1)>]
value: DensityMatrix([[ 0.60504462+0.00000000e+00j,  0.09641433-1.21378845e-01j,
                -0.00609624+1.25240694e-01j, -0.05764839-3.94787461e-01j],
               [ 0.09641433+1.21378845e-01j,  0.0496681 +3.46944695e-18j,
                -0.01597784+2.71278286e-02j,  0.05783043-5.90366544e-02j],
               [-0.00609624-1.25240694e-01j, -0.01597784-2.71278286e-02j,
                 0.04334793+0.00000000e+00j, -0.08050319+4.18747694e-02j],
               [-0.05764839+3.94787461e-01j,  0.05783043+5.90366544e-02j,
                -0.08050319-4.18747694e-02j,  0.30193935+0.00000000e+00j]],
              dims=(2, 2))
quality: None
backend: aer_simulator_from(fake_perth)
run_time: None
trace: 1.0000000000000004
eigvals: [0.94247343 0.05752657 0.         0.        ]
raw_eigvals: [ 1.05272683  0.16777997 -0.04624811 -0.17425869]
rescaled_psd: True
fitter_metadata: {'fitter': 'linear_inversion', 'fitter_time': 0.0027294158935546875}
conditional_probability: 1.0
positive: True

Tomography Fitters

The default fitters is linear_inversion, which reconstructs the state using dual basis of the tomography basis. This will typically result in a non-positive reconstructed state. This state is rescaled to be positive-semidefinite (PSD) by computing its eigen-decomposition and rescaling its eigenvalues using the approach from Ref. [1].

There are several other fitters are included (See API documentation for details). For example, if cvxpy is installed we can use the cvxpy_gaussian_lstsq() fitter, which allows constraining the fit to be PSD without requiring rescaling.

try:
    import cvxpy

    # Set analysis option for cvxpy fitter
    qstexp1.analysis.set_options(fitter='cvxpy_gaussian_lstsq')

    # Re-run experiment
    qstdata2 = qstexp1.run(backend, seed_simulation=100).block_for_results()

    state_result2 = qstdata2.analysis_results("state", dataframe=True).iloc[0]
    for key, val in state_result2.items():
        print(f"{key}: {val}")

except ModuleNotFoundError:
    print("CVXPY is not installed")
name: state
experiment: StateTomography
components: [<Qubit(Q0)>, <Qubit(Q1)>]
value: DensityMatrix([[ 4.65969162e-01+0.j        ,  1.43102172e-03+0.00270069j,
                 2.65124120e-04-0.00725298j, -2.81659958e-04-0.44280944j],
               [ 1.43102172e-03-0.00270069j,  3.32110179e-02+0.j        ,
                 2.52513431e-02-0.00249522j, -1.48844469e-03+0.01073149j],
               [ 2.65124120e-04+0.00725298j,  2.52513431e-02+0.00249522j,
                 3.39172505e-02+0.j        , -6.28704230e-03-0.00630811j],
               [-2.81659958e-04+0.44280944j, -1.48844469e-03-0.01073149j,
                -6.28704230e-03+0.00630811j,  4.66902570e-01+0.j        ]],
              dims=(2, 2))
quality: None
backend: aer_simulator_from(fake_perth)
run_time: None
trace: 1.000000004328557
eigvals: [0.90932837 0.06054541 0.0282295  0.00189672]
raw_eigvals: [0.90932836 0.0605454  0.0282295  0.00189672]
rescaled_psd: False
fitter_metadata: {'fitter': 'cvxpy_gaussian_lstsq', 'cvxpy_solver': 'SCS', 'cvxpy_status': ['optimal'], 'psd_constraint': True, 'trace_preserving': True, 'fitter_time': 0.03091883659362793}
conditional_probability: 1.0
positive: True

Parallel Tomography Experiment

We can also use the ParallelExperiment class to run subsystem tomography on multiple qubits in parallel.

For example if we want to perform 1-qubit QST on several qubits at once:

from math import pi
num_qubits = 5
gates = [qiskit.circuit.library.RXGate(i * pi / (num_qubits - 1))
         for i in range(num_qubits)]

subexps = [
    StateTomography(gate, physical_qubits=(i,))
    for i, gate in enumerate(gates)
]
parexp = ParallelExperiment(subexps)
pardata = parexp.run(backend, seed_simulation=100).block_for_results()

display(pardata.analysis_results(dataframe=True))
name experiment components value quality backend run_time trace eigvals raw_eigvals rescaled_psd fitter_metadata conditional_probability positive
d95b0acf state StateTomography [Q0] DensityMatrix([[ 0.97070312+0.j , -0.01... None aer_simulator_from(fake_perth) None 1.0 [0.9709563148147258, 0.0290436851852751] [0.9709563148147258, 0.0290436851852751] False {'fitter': 'linear_inversion', 'fitter_time': ... 1.0 True
0eaad757 state_fidelity StateTomography [Q0] 0.970703 None aer_simulator_from(fake_perth) None None None None None None None None
f7fcbaf4 positive StateTomography [Q0] True None aer_simulator_from(fake_perth) None None None None None None None None
fecfd2d5 state StateTomography [Q1] DensityMatrix([[0.82617188+0.j , 0.0185... None aer_simulator_from(fake_perth) None 1.0 [0.9595844647603133, 0.04041553523968769] [0.9595844647603133, 0.04041553523968769] False {'fitter': 'linear_inversion', 'fitter_time': ... 1.0 True
832d7a77 state_fidelity StateTomography [Q1] 0.959205 None aer_simulator_from(fake_perth) None None None None None None None None
a065aea8 positive StateTomography [Q1] True None aer_simulator_from(fake_perth) None None None None None None None None
07598980 state StateTomography [Q2] DensityMatrix([[0.49707031+0.j , 0.0156... None aer_simulator_from(fake_perth) None 1.0 [0.9573076504612736, 0.042692349538727026] [0.9573076504612736, 0.042692349538727026] False {'fitter': 'linear_inversion', 'fitter_time': ... 1.0 True
9afd4678 state_fidelity StateTomography [Q2] 0.957031 None aer_simulator_from(fake_perth) None None None None None None None None
f79fba9b positive StateTomography [Q2] True None aer_simulator_from(fake_perth) None None None None None None None None
60205ef3 state StateTomography [Q3] DensityMatrix([[0.16796875+0.j , 0.0068... None aer_simulator_from(fake_perth) None 1.0 [0.9654885316085481, 0.03451146839145283] [0.9654885316085481, 0.03451146839145283] False {'fitter': 'linear_inversion', 'fitter_time': ... 1.0 True
21b08a78 state_fidelity StateTomography [Q3] 0.96542 None aer_simulator_from(fake_perth) None None None None None None None None
6c2fc06d positive StateTomography [Q3] True None aer_simulator_from(fake_perth) None None None None None None None None
57e38029 state StateTomography [Q4] DensityMatrix([[0.02636719+0.j , 0.0087... None aer_simulator_from(fake_perth) None 1.0 [0.9737234127263885, 0.026276587273611946] [0.9737234127263885, 0.026276587273611946] False {'fitter': 'linear_inversion', 'fitter_time': ... 1.0 True
2f76f92f state_fidelity StateTomography [Q4] 0.973633 None aer_simulator_from(fake_perth) None None None None None None None None
7a67345d positive StateTomography [Q4] True None aer_simulator_from(fake_perth) None None None None None None None None

View experiment analysis results for one component:

results = pardata.analysis_results(dataframe=True)
display(results[results.components.apply(lambda x: x == ["Q0"])])
name experiment components value quality backend run_time trace eigvals raw_eigvals rescaled_psd fitter_metadata conditional_probability positive
d95b0acf state StateTomography [Q0] DensityMatrix([[ 0.97070312+0.j , -0.01... None aer_simulator_from(fake_perth) None 1.0 [0.9709563148147258, 0.0290436851852751] [0.9709563148147258, 0.0290436851852751] False {'fitter': 'linear_inversion', 'fitter_time': ... 1.0 True
0eaad757 state_fidelity StateTomography [Q0] 0.970703 None aer_simulator_from(fake_perth) None None None None None None None None
f7fcbaf4 positive StateTomography [Q0] True None aer_simulator_from(fake_perth) None None None None None None None None

References

See also