Introduction to dynamic circuits

Note

Click here to view this tutorial as an interactive Jupyter notebook in IBM Quantum Lab (requires sign-in).

This tutorial introduces the current dynamic circuit support through Qiskit on IBM Quantum hardware. This will change quickly as we develop this functionality, and we will aim to keep this tutorial up-to-date with the latest features and functionality.

Preamble

import os
from typing import Any, List, Dict, Union

import numpy as np
import matplotlib.pyplot as plt

from qiskit import IBMQ, QuantumCircuit, QuantumRegister, ClassicalRegister, quantum_info as qi
from qiskit.providers.ibmq import RunnerResult
from qiskit.result import marginal_counts
import qiskit.tools.jupyter

%matplotlib inline

import warnings
warnings.filterwarnings("ignore")

Run OpenQASM 3 with Qiskit

Note

Before running this notebook, make sure to the follow the steps in the Hello Dynamic Circuits notebook, to verify you are set up to run dynamic circuit programs on IBM hardware.

Step 1: Load backend

# Note: This can be any hub/group/project that has access to the required device and the Qiskit runtime.
# Verify that ``qasm3`` is present in ``backend.configuration().supported_features``.
# hub = "<hub>"
# group = "<group>"
# project = "<project>"
# backend_name = "<your backend>"
from qiskit import IBMQ
IBMQ.load_account()
provider = IBMQ.get_provider(hub=hub, group=group, project=project)
backend = provider.get_backend(backend_name)
# To get more information about your backend
import qiskit.tools.jupyter
backend

Step 2: Write a quantum circuit with Qiskit

By integrating OpenQASM 3 with the QuantumCircuit interface, we gain access to the extensive suite of Qiskit algorithms and applications.

from qiskit import QuantumCircuit

qc_prep_excited = QuantumCircuit(1, 1)
qc_prep_excited.x(0)
qc_prep_excited.measure(0, 0)
qc_prep_excited.draw(output="mpl", idle_wires=False)
../../../../_images/output_10_03.png

Step 3: Transpile the circuit to target it to the backend

qubit = 0
shots = 1000
from qiskit import transpile

qc_prep_excited_transpiled = transpile(qc_prep_excited, backend, initial_layout=[qubit])

qc_prep_excited_transpiled.draw(output="mpl", idle_wires=False)
../../../../_images/output_13_03.png

Step 4: Import a routine and run it

from run_openqasm3 import run_openqasm3

job = run_openqasm3(qc_prep_excited_transpiled, backend, verbose=True, shots=shots, init_num_resets=0, init_delay=0)  # We disable qubit initialization for now.
=======circuit-0=======
=======OpenQASM 3======
OPENQASM 3;
bit[1] c;
x $0;
c[0] = measure $0;

==================
               ┌───┐┌─┐
      q_0 -> 0 ┤ X ├┤M├
               └───┘└╥┘
ancilla_0 -> 1 ──────╫─
                     ║
ancilla_1 -> 2 ──────╫─
                     ║
ancilla_2 -> 3 ──────╫─
                     ║
ancilla_3 -> 4 ──────╫─
                     ║
ancilla_4 -> 5 ──────╫─
                     ║
ancilla_5 -> 6 ──────╫─
                     ║
          c: 1/══════╩═
                     0
==================
Running: caosn3lk1m9sjqungbig
=======circuit-0=======
{'0': 41, '1': 959}

What is going on in the background?

Note

In a future release of the qiskit-ibm-provider, we will release an updated interface in the form of backend.run(circuit) which invokes the Qiskit Runtime.

We are submitting circuit jobs to a Qiskit Runtime program that exposes a backend.run(circuits, ...) like interface, but which accepts either QuantumCircuit or OpenQASM 3 strings for circuits.

Qiskit Runtime

Qiskit Runtime

Submit directly to the OpenQASM 3 runtime program

Step 0: Let’s make sure we have the OpenQASM 3 runtime program available to us

programs = provider.runtime.programs()
print(f"The runtime programs we have available to us are: {list(program.name for program in programs)}")
The runtime programs we have available to us are: ['hello-world', 'qaoa', 'torch-train', 'torch-infer', 'qasm3-runner', 'sampler', 'estimator', 'sample-expval', 'vqe', 'circuit-runner', 'sample-program', 'quantum-kernel-alignment']

We will use qasm3-runner, which is set up to run your programs in the new dynamic circuits software and hardware stack.

qasm3_runner = next(program for program in programs if program.name == "qasm3-runner")

print("The qasm3-runner accepts the following program runtime parameters:")
print(qasm3_runner.parameters())
The qasm3-runner accepts the following program runtime parameters:
ParameterNamespace (Values):
| Name       | Value        | Type         | Required |     Description |
-------------------------------------------------------------------------
| circuits   | None         | ['object', '| True     | A circuit/OpenQASM 3 string or a list of circuits/OpenQASM 3 strings (real backend only). |
| exporter_c | None         | object      | False    | Options to use when converting circuits to QASM3 strings, if applicable. 'disable_constants' is set to True by default. |
| init_circu | None         | ['object']  | False    | A quantum circuit to execute for initializing qubits before each circuit. If specified, parameters "init_num_resets" and "init_delay" are ignored. |
| init_delay | None         | int         | False    | The number of microseconds of delay to insert before each circuit execution. |
| init_num_r | None         | int         | False    | The number of qubit resets to insert before each circuit execution. |
| merge_circ | None         | boolean     | False    | Whether to merge multiple QuantumCircuits into one single QuantumCircuit containing each individual circuit separated by an initialization circuit. This will greatly improve the performance of your runtime program and should almost always be used. The default initialization circuit is a series of qubit resets (the number of which may be configured with "init_num_resets" ) on qubits used in your experiment followed by a relaxation delay (Which may be specified with "init_delay". You may also provide your own initialization circuit through "init_circuit". |
| qasm3_args | None         | ['object', '| False    | Input arguments to the OpenQASM 3 program. Only supported when running on a simulator. |
| run_config | None         | object      | False    | Execution time options. QASM 3 simulator supports 'shots', and real backends support both 'shots' and 'rep_delay'. |
| skip_trans | None         | boolean     | False    | Skip circuit transpilation. |
| transpiler | None         | object      | False    | Compilation options. |
| use_measur | None         | boolean     | False    | Whether to apply measurement error mitigation. Currently, error mitigation only applies to QuantumCircuit. |

Note that the qasm3-runner will merge all circuits you submit into one large circuit, with qubits initializations inserted in between each execution. If you do not desire this behavior, you can set "merge_circuits": False; however, this will drastically slow down execution time because the backend must then compile, load, and execute each circuit individually.

You may control the behavior of the qubit initialization through the parameters init_num_resets and init_delay, or even supply your own initialization circuit to init_circuit.

Step 1: Build program arguments and options

For now, we won’t reset our qubits automatically and will instead program this behavior ourselves.

runtime_params = {
    'circuits': [qc_prep_excited_transpiled],
    "run_config": {"shots": shots},
    "init_num_resets": 0, # ensure we do not initialize our qubits automatically
    "init_delay": 0,
}

options = {'backend_name': backend.name()}

Step 2: Submit job to the qasm3-runner runtime

from qiskit.providers.ibmq import RunnerResult

prep_excited_job = provider.runtime.run(
    program_id="qasm3-runner",
    options=options,
    inputs=runtime_params,
    result_decoder=RunnerResult,
)

The runtime returns a job handle.

Note that the RunnerResult decoder we’ve passed to the runtime handles decoding our results to the standard Qiskit result format. In general, runtimes can return any JSON output, provided an appropriate decoder is available.

Fetching the result is just like a normal Qiskit job result.

prep_excited_result = prep_excited_job.result()
print(prep_excited_result.get_counts())
{'0': 60, '1': 940}

Let’s compare the results with automatic qubit initialization.

runtime_init_params = {
    'circuits': [qc_prep_excited_transpiled],
    "run_config": {"shots": shots},
}
from qiskit.providers.ibmq import RunnerResult

prep_excited_init_job = provider.runtime.run(
    program_id="qasm3-runner",
    options=options,
    inputs=runtime_init_params,
    result_decoder=RunnerResult,
)
prep_excited_init_result = prep_excited_init_job.result()
print(f"Results without initialization: {prep_excited_result.get_counts()}")
print(f"Results with initialization: {prep_excited_init_result.get_counts()}")
Results without initialization: {'0': 60, '1': 940}
Results with initialization: {'0': 14, '1': 986}

Submit OpenQASM 3 strings directly

This is useful for the cases where QuantumCircuit does not yet support all of the semantics of OpenQASM 3. To see what features of OpenQASM 3 are currently supported in hardware, please see the feature support table here.

We can get the corresponding OpenQASM 3 description of our circuit from Qiskit.

Note

We cannot use circuit-merging when OpenQASM 3 source strings are submitted. In this case, each OpenQASM 3 source will be run individually.

try:
    from qiskit import qasm3
except ImportError:
    print("You likely do not have the main branch of Qiskit installed. Please install it to run this code")
    raise
basis_gates = backend.configuration().basis_gates

def dump_qasm3(circuit, backend=backend):
    return qasm3.Exporter(includes=[],
                          basis_gates=backend.configuration().basis_gates,
                          disable_constants=True).dumps(circuit)

excited_prep_source_string = dump_qasm3(qc_prep_excited_transpiled)
print(excited_prep_source_string)
OPENQASM 3;
bit[1] c;
x $0;
c[0] = measure $0;

For the rest of the tutorial we will use the method run_openqasm3, which is a simple wrapper around the runtime calls above with some additional logging.

runtime_job = run_openqasm3(excited_prep_source_string,
                        backend, verbose=True,
                        shots=shots, init_num_resets=0,
                        init_delay=0)  # Turn off automatic init
{'0': 29, '1': 971}

Write conditional reset directly

We want to provide more expressive semantics to our users, so they can explore quantum error correction and other applications requiring real-time compute.

from qiskit import QuantumRegister, ClassicalRegister

qr = QuantumRegister(1)
crx = ClassicalRegister(1, name="xresult")
crm = ClassicalRegister(1, name="measureresult")
qc_reset = QuantumCircuit(qr, crx, crm, name="Reset")
qc_reset.x(0)
qc_reset.measure(0, crx)
qc_reset.x(0).c_if(crx, 1)
qc_reset.measure(0, crm)

qc_reset = transpile(qc_reset, backend, initial_layout=[0])
qc_reset.draw(output="mpl", idle_wires=False)
../../../../_images/output_43_03.png

Alternatively, we can write as a OpenQASM 3 program.

print(dump_qasm3(qc_reset))
OPENQASM 3;
bit[1] xresult;
bit[1] measureresult;
x $0;
xresult[0] = measure $0;
if (xresult == 1) {
  x $0;
}
measureresult[0] = measure $0;

Then, run the program.

reset_job = run_openqasm3(qc_reset, backend, verbose=True, shots=shots, init_num_resets=0, init_delay=0) # Turn off automatic init
=======Reset=======
=======OpenQASM 3======
OPENQASM 3;
bit[1] xresult;
bit[1] measureresult;
x $0;
xresult[0] = measure $0;
if (xresult == 1) {
  x $0;
}
measureresult[0] = measure $0;

==================
                 ┌───┐┌─┐ ┌───┐ ┌─┐
       q6_0 -> 0 ┤ X ├┤M├─┤ X ├─┤M├
                 └───┘└╥┘ └─╥─┘ └╥┘
  ancilla_0 -> 1 ──────╫────╫────╫─
                       ║    ║    ║
  ancilla_1 -> 2 ──────╫────╫────╫─
                       ║    ║    ║
  ancilla_2 -> 3 ──────╫────╫────╫─
                       ║    ║    ║
  ancilla_3 -> 4 ──────╫────╫────╫─
                       ║    ║    ║
  ancilla_4 -> 5 ──────╫────╫────╫─
                       ║    ║    ║
  ancilla_5 -> 6 ──────╫────╫────╫─
                       ║ ┌──╨──┐ ║
      xresult: 1/══════╩═╡ 0x1 ╞═╬═
                       0 └─────┘ ║
measureresult: 1/════════════════╩═
                                 0
==================
Running: caosntvhrqeo3qj6lvf0
=======Reset=======
{'0 0': 29, '0 1': 924, '1 0': 4, '1 1': 43}

Now, let’s understand the results.

from qiskit.result import marginal_counts

reset_result = reset_job.result()
reset_counts = reset_result.get_counts(0)
mitigated_reset_results = marginal_counts(reset_counts, indices=[1])
print(f"Full counts including reset: {reset_counts}")
print(f"Results from our reset - |0>s prepared {mitigated_reset_results.get('0')}, |1>s prepared {mitigated_reset_results['1']}"
)
Full counts including reset: {'0 0': 29, '0 1': 924, '1 0': 4, '1 1': 43}
Results from our reset - |0>s prepared 953, |1>s prepared 47

Let’s try several rounds of reset by submitting a OpenQASM 3 source string directly.

qubit_init_qasm3 = """

OPENQASM 3;

bit reset_result0;
bit reset_result1;
bit reset_result2;
bit[1] measureresult;

x $0;
reset_result0 = measure $0;

// three rounds of reset.
if (reset_result0 == 1) {
    x $0;
}

reset_result1 = measure $0;

if (reset_result1 == 1) {
    x $0;
}

reset_result2 = measure $0;

if (reset_result2 == 1) {
    x $0;
}

// Final measurement
measureresult[0] = measure $0;

"""
init_job = run_openqasm3(qubit_init_qasm3, backend, verbose=False, shots=shots, init_num_resets=0, init_delay=0)
print(init_job.job_id())
better_reset_result_1s = sum(count for key, count in init_job.result().get_counts(0).items() if (len(key)==4))
print(f"Results from our better reset - |0>s prepared {shots-better_reset_result_1s}, |1>s prepared {better_reset_result_1s} ")
caoso1vhrqeo3qj6lvq0
Results from our better reset - |0>s prepared 994, |1>s prepared 6

Teleport a quantum state

Visit the Qiskit Textbook to learn more about Quantum teleportation.

../../../../_images/telamon3.jpeg

Step 1: Prepare the teleportation circuit

qr = QuantumRegister(3)
crz = ClassicalRegister(1, name="crz")
crx = ClassicalRegister(1, name="crx")
result = ClassicalRegister(1, name="result")

qc_teleport = QuantumCircuit(qr, crz, crx, result, name="Teleport")

# Apply teleportation circuit
qc_teleport.h(qr[1])
qc_teleport.cx(qr[1], qr[2])
qc_teleport.cx(qr[0], qr[1])
qc_teleport.h(qr[0])
qc_teleport.measure(qr[0], crz)
qc_teleport.measure(qr[1], crx)
qc_teleport.z(qr[2]).c_if(crz, 1)
qc_teleport.x(qr[2]).c_if(crx, 1)
qc_teleport.draw(output="mpl")
../../../../_images/output_55_03.png

Step 2: Prepare the teleport experiment circuit

# Prepare |+> for teleportation
qc_state_prep = QuantumCircuit(1)
qc_state_prep.h(0)
print(qc_state_prep)

target_state_prep = qi.Statevector.from_instruction(qc_state_prep)
print(target_state_prep)
   ┌───┐
q: ┤ H ├
   └───┘
Statevector([0.70710678+0.j, 0.70710678+0.j],
            dims=(2,))

Step 3: Compose the state initialization with the teleportation

teleport_qubits = [0, 1, 2]

qc_teleport_state = QuantumCircuit(qr, crz, crx, result, name="Teleport Hadamard")

# Prepare state to teleport
qc_teleport_state.compose(qc_state_prep, [qr[0]], inplace=True)
qc_teleport_state.barrier(qr)

# Compose with teleportation circuit
qc_teleport_state.compose(qc_teleport, inplace=True)

qc_teleport_state.draw(output="mpl")
../../../../_images/output_59_03.png

Step 4: Transpile for backend and execute

qc_teleport_experiment = qc_teleport_state.copy()
qc_teleport_experiment.measure(qr[2], result)
qc_teleport_experiment = transpile(qc_teleport_experiment, backend, initial_layout=teleport_qubits, optimization_level=3)
qc_teleport_experiment.draw(output="mpl")
../../../../_images/output_61_03.png

Step 5: Run our teleportation experiment

sim = False
from qiskit import Aer
backend_sim = Aer.get_backend('aer_simulator')

if not sim:
    teleport_job = run_openqasm3(qc_teleport_experiment, backend, verbose=False, shots=shots)
else:
    teleport_job = backend_sim.run(qc_teleport_experiment, shots=shots)

print(teleport_job.job_id())
caoso65hr4d5d8vkj2m0
teleport_result = teleport_job.result()
print(f"All teleportation reults: {teleport_result.get_counts(0)}")

marginal_teleport_counts = marginal_counts(teleport_result.get_counts(0), indices=[0])
print(f"Marginalized teleportation counts: {marginal_teleport_counts}")
All teleportation reults: {'0 0 0': 258, '0 0 1': 212, '0 1 0': 50, '0 1 1': 44, '1 0 0': 178, '1 0 1': 127, '1 1 0': 78, '1 1 1': 53}
Marginalized teleportation counts: {'0': 564, '1': 436}

Step 6: Measurement Error Mitigation

The measurement calibration is used to mitigate measurement errors. The main idea is to prepare all 2𝑛 basis input states and compute the probability of measuring counts in the other basis states. From these calibrations, it is possible to correct the average results of another experiment of interest.

As integration is done with Qiskit, we still take advantage of Qiskit’s QCVV infrastructure. Next, we’ll use measurement error mitigation to correct the results of our final measurements.

from qiskit.ignis.verification.tomography import state_tomography_circuits, StateTomographyFitter
from qiskit.ignis.verification.tomography import process_tomography_circuits, ProcessTomographyFitter
from qiskit.ignis.mitigation.measurement import complete_meas_cal, CompleteMeasFitter
from qiskit.visualization import plot_state_city, plot_state_paulivec
meas_calibs, state_labels = complete_meas_cal(qr=[qr[2]], circlabel='mcal')
meas_calibs = transpile(meas_calibs, backend, initial_layout={qr[2]: teleport_qubits[-1]})
meas_calibs[1].draw(idle_wires=False, output="mpl")
../../../../_images/output_69_03.png

Step 7: Run mitigation circuits and extract results

if not sim:
    meas_calib_job = run_openqasm3(meas_calibs, backend, verbose=False, shots=shots)
else:
    meas_calib_job = backend_sim.run(meas_calibs, shots=shots)

print(f"Job id: {meas_calib_job.job_id()}")
Job id: caosoa5hr4d5d8vkj30g
meas_calib_result = meas_calib_job.result()
meas_fitter = CompleteMeasFitter(meas_calib_result, state_labels, circlabel='mcal')
meas_fitter.cal_matrix
array([[0.972, 0.022],
       [0.028, 0.978]])
meas_filter = meas_fitter.filter
mitigated_teleport_counts = meas_filter.apply(marginal_teleport_counts)

print(f"Unmitigated teleportation counts {marginal_teleport_counts}")
print(f"Mitigated teleportation counts {mitigated_teleport_counts}")
Unmitigated teleportation counts {'0': 564, '1': 436}
Mitigated teleportation counts {'0': 570.5263960135052, '1': 429.4736039864948}

Step 8: Perform quantum state tomography (QST)

Given a state-preparation circuit that prepares a system in a state, reconstruct a description of the density matrix 𝜌 of the actual state obtained in the system.

Next we’ll use Qiskit Ignis to apply state tomography to our teleported qubit to verify that we teleported a coherent quantum state.

Note

The performance of state tomography is currently limited by T2*-induced dephasing, to which the teleportation circuit is particularly sensitive, as it leaves the input qubit in a coherent state for a long idle period. This will be mitigated with the use of dynamical decoupling, for which support is being developed for dynamic circuits in Qiskit.

def build_state_tomo_circuits(qc_prep, qr, clbits, tomo_qubits, layout):
    qst_tomography_circuits = state_tomography_circuits(QuantumCircuit(qr), tomo_qubits)

    tomography_circuits = []
    for tomo_circuit in qst_tomography_circuits:
        tomo_circ = qc_prep.compose(tomo_circuit, clbits=clbits)
        tomo_circ.name = tomo_circuit.name
        tomography_circuits.append(tomo_circ)

    return transpile(tomography_circuits, backend, initial_layout=layout)
qt_qst_tomography_circuits = build_state_tomo_circuits(qc_teleport_state, qr, [result[0]], [qr[2]], teleport_qubits)

Step 9: Run QST teleportation

from qiskit import Aer
backend_sim = Aer.get_backend('aer_simulator')

if not sim:
    teleport_qst_jobs = run_openqasm3(qt_qst_tomography_circuits, backend, verbose=False, shots=shots)
else:
    teleport_qst_jobs = backend_sim.run(qt_qst_tomography_circuits, shots=shots)
qt_qst_tomography_circuits[1].draw(output="mpl")
../../../../_images/output_80_03.png

Step 10: Fit QST results

teleport_qst_result = teleport_qst_jobs.result()
marginal_teleport_qst_result = marginal_counts(teleport_qst_result, indices=[2])
mitigated_qst_results = meas_filter.apply(marginal_teleport_qst_result)
qt_qst_tomo_fitter = StateTomographyFitter(mitigated_qst_results, qt_qst_tomography_circuits)
qt_qst_rho_fit = qt_qst_tomo_fitter.fit(method='lstsq')
qt_qst_F = qi.state_fidelity(qt_qst_rho_fit, target_state_prep)
print('State Fidelity: F = {:.5f}'.format(qt_qst_F))
State Fidelity: F = 0.60737

Step 11: Pauli vector plot of the teleported density matrix

fig, axes = plt.subplots(1, 2, sharey=True)
plot_state_paulivec(qt_qst_rho_fit, ax=axes[0]);
plot_state_paulivec(qi.DensityMatrix.from_instruction(qc_state_prep), ax=axes[1])
axes[1].set_ylabel(None);
axes[0].set_title("Experiment")
axes[1].set_title("Ideal")
plt.tight_layout()
fig.set_size_inches(14, 6)
../../../../_images/output_86_03.png
plot_state_city(qt_qst_rho_fit, title="Experiment");
plot_state_city(qi.DensityMatrix.from_instruction(qc_state_prep), title="Ideal");

Step 12: Perform process tomography

Note

The performance of process tomography is currently limited by T2*-induced dephasing, to which the teleportation circuit is particularly sensitive, as it leaves the input qubit in a coherent state for a long idle period. This will be mitigated with the use of dynamical decoupling, for which support is being developed for dynamic circuits in Qiskit.

def build_process_tomo_circuits(qc_teleport, qr, clbits, layout):
    qst_tomography_circuits = process_tomography_circuits(qc_teleport, qr[2], prepared_qubits=qr[0])
    return transpile(qst_tomography_circuits, backend, initial_layout=layout)
qt_qpt_tomography_circuits = build_process_tomo_circuits(qc_teleport, qr, [crz, crx, result], teleport_qubits)

Step 13: Run QPT Teleportation

if not sim:
    teleport_qpt_jobs = run_openqasm3(qt_qpt_tomography_circuits, backend, verbose=False, shots=shots)
else:
    teleport_qpt_jobs = backend_sim.run(qt_qpt_tomography_circuits, shots=shots)

Step 14: Fit QPT results

teleport_qpt_result = teleport_qpt_jobs.result()
marginal_teleport_qpt_result = marginal_counts(teleport_qpt_result, indices=[0]) # Note this is a consequence of https://internal-docs.quantum-computing.ibm.com/vt-dynamic-circuits/dynamic_circuits/faq.html#why-are-my-results-appearing-out-of-order
mitigated_qpt_results = meas_filter.apply(marginal_teleport_qpt_result)
qt_qpt_tomo_fitter = ProcessTomographyFitter(mitigated_qpt_results, qt_qpt_tomography_circuits)
qt_qpt_tomo_fitter.data
{(('Zp',), ('X',)): {'0': 500.00000191769726, '1': 499.9999980823027},
 (('Zp',), ('Y',)): {'0': 487.36843524446704, '1': 512.631564755533},
 (('Zp',), ('Z',)): {'0': 711.5788849292722, '1': 288.4211150707277},
 (('Zm',), ('X',)): {'0': 522.1052614700847, '1': 477.89473852991523},
 (('Zm',), ('Y',)): {'0': 498.9473692695451, '1': 501.0526307304547},
 (('Zm',), ('Z',)): {'0': 371.57895013703273, '1': 628.4210498629673},
 (('Xp',), ('X',)): {'0': 604.2106483873631, '1': 395.7893516126369},
 (('Xp',), ('Y',)): {'0': 590.5262629214875, '1': 409.47373707851244},
 (('Xp',), ('Z',)): {'0': 534.7368410136405, '1': 465.2631589863595},
 (('Yp',), ('X',)): {'0': 449.4736482264551, '1': 550.5263517735449},
 (('Yp',), ('Y',)): {'0': 605.2630262939948, '1': 394.73697370600524},
 (('Yp',), ('Z',)): {'0': 562.1052248074989, '1': 437.8947751925011}}
choi_fit_lstsq = qt_qpt_tomo_fitter.fit(method='lstsq')
qpt_chi = qi.Chi(choi_fit_lstsq)
print('Average gate fidelity: F = {:.5f}'.format(qi.average_gate_fidelity(choi_fit_lstsq, target=qi.Pauli("I"))))
Average gate fidelity: F = 0.62509
figsize = (7, 5)
fig, ax = plt.subplots(1, 1, figsize=figsize)
data = (qpt_chi.data).flatten()
labels = qiskit.quantum_info.pauli_basis(2).to_labels()


color = "#648fff"

ind = np.arange(len(data))  # the x locations for the groups
width = 0.5  # the width of the bars
ax.grid(zorder=0, linewidth=1, linestyle="--")
ax.bar(ind, data, width, color=color, zorder=2)
ax.axhline(linewidth=1, color="k")
# add some text for labels, title, and axes ticks
ax.set_ylabel("Expectation value", fontsize=14)
ax.set_xticks(ind)
ax.set_yticks([-1, -0.5, 0, 0.5, 1])
ax.set_xticklabels(labels, fontsize=14, rotation=70)
ax.set_xlabel("Pauli", fontsize=14)
ax.set_ylim([-2, 2])
ax.set_facecolor("#eeeeee")
ax.set_xticklabels(labels, fontsize=14, rotation=70)
fig.suptitle("Characterization of quantum teleportation identity channel - Chi matrix visualization ($\chi(QPT)$)")
ax.set_title(f"{backend_name} qubits={teleport_qubits} - Average gate fidelity: F = {qi.average_gate_fidelity(choi_fit_lstsq, target=qi.Pauli('I')):.5f}")
../../../../_images/output_98_13.png
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
qiskit-terra0.21.0
qiskit-aer0.10.4
qiskit-ignis0.7.0
qiskit-ibmq-provider0.20.0.dev0+4f1f8c6
qiskit0.36.1
System information
Python version3.9.5
Python compilerClang 10.0.0
Python builddefault, May 18 2021 12:31:01
OSDarwin
CPUs8
Memory (Gb)32.0
Tue Jun 21 09:49:18 2022 EDT

This code is a part of Qiskit

© Copyright IBM 2017, 2022.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.