Download

Download this file as Jupyter notebook: qiskit_submission_example.ipynb.

Example: Running Jobs on Qiskit Backends

True-Q™ supports direct execution of circuit collections on hardware and simulators that use a Qiskit backend. This includes the IBMQ superconducting qubit chips . Transpiling to the given backend is done automatically for any backend whose gates are a subset of ['id', 'rz', 'sx', 'x', 'cx'] [1].

Qiskit backends accept job requests that contain one or more circuits to be run at a specified number of shots. Like many modern quantum hardware platforms, these backends have restrictions on how many circuits a user can submit per job, and jobs are prioritized in a queuing system that contains many users’ jobs. Thus, large circuits collections (e.g. as generated by True-Q™ diagnostic protocols) must be batched into multiple jobs.

The True-Q™ Executor class automates the batching and job submission process according to the above restrictions. An asynchronous thread monitors the status of each submitted job, and submits new jobs as spots open in the queue.

[2]:
import trueq as tq
import qiskit as qk

Running jobs on a Qiskit backend requires credentials for the provider of the backend. See the provider’s documentation for instructions for how to set this up. For example, the following snippet demonstrates how one instantiates a remote backend object from the IBM Quantum Experience. This example file does not have any credentials, so we make do with the local Qiskit simulator which uses the same backend abstraction.

[3]:
have_credentials = False

if have_credentials:
    qk.IBMQ.load_account()
    provider = qk.IBMQ.get_provider()
    backend = provider.get_backend("ibmq_manila")
else:
    from qiskit_aer import AerSimulator
    from qiskit.providers.fake_provider import FakeMelbourneV2

    backend = AerSimulator.from_backend(FakeMelbourneV2())
Warning: The class ``qiskit.providers.fake_provider.fake_backend.FakeBackendV2`` is deprecated as of qiskit 0.46.0. It will be removed in qiskit 1.0. All fake backend instances based on real device snapshots (`FakeVigo`,`FakeSherbrooke`,...) have been migrated to the `qiskit_ibm_runtime` package. To migrate your code, run `pip install qiskit-ibm-runtime` and use `from qiskit_ibm_runtime.fake_provider import FakeExample` instead of `from qiskit.providers.fake_provider import FakeExample`.
         (/tmp/ipykernel_310089/41091723.py:11)

Submitting a Circuit

The Executor accepts both single Circuits and CircuitCollections. For example:

[4]:
# Define a simple 2-qubit circuit to work with.
circuit = tq.Circuit([{0: tq.Gate.h}, {(0, 1): tq.Gate.cx}])
circuit.measure_all()

ex = tq.interface.Executor(circuit, backend, n_shots=128)

# the executor is asynchronous, call a blocking function to wait for it to finish
ex.block()
True-Q formatting will not be loaded without trusting this notebook or rerunning the affected cells. Notebooks can be marked as trusted by clicking "File -> Trust Notebook".
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[4], line 5
      2 circuit = tq.Circuit([{0: tq.Gate.h}, {(0, 1): tq.Gate.cx}])
      3 circuit.measure_all()
----> 5 ex = tq.interface.Executor(circuit, backend, n_shots=128)
      7 # the executor is asynchronous, call a blocking function to wait for it to finish
      8 ex.block()

File ~/workspace/release trueq/trueq/interface/qiskit_submission.py:629, in Executor.__init__(self, circuits, backend, filename, n_shots, max_submissions, overwrite, store_compiled)
    627         extra = set(count_streaks(circuit)).difference(config.connectivity)
    628         if len(extra) > 0:
--> 629             raise ValueError(
    630                 f"A circuit specifies gates between the qubits {extra}, but "
    631                 f"these qubits are not connected on {backend.name}."
    632             )
    634 # compile circuits, initialize batch executors
    635 logger.debug("Executor - Compiling circuits and creating batches")

ValueError: A circuit specifies gates between the qubits {frozenset({0, 1})}, but these qubits are not connected on aer_simulator_from(fake_melbourne).

The Executor automatically populates the Results attribute of the submitted circuit:

[5]:
print(circuit.results)
circuit.results.plot()
Results({}, dim=None)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[5], line 2
      1 print(circuit.results)
----> 2 circuit.results.plot()

File ~/workspace/release trueq/trueq/results.py:645, in Results.plot(self, sparse_cutoff, axis, error_bars)
    619 r"""
    620 Plots a bar graph of these results. See also
    621 :py:func:`~trueq.visualization.plot_results` for plotting several
   (...)
    641 :type error_bars: :py:class:`bool`
    642 """
    643 from trueq.visualization.general import plot_results
--> 645 plot_results(
    646     self, sparse_cutoff=sparse_cutoff, axis=axis, error_bars=error_bars
    647 )

File ~/workspace/release trueq/trueq/visualization/general.py:154, in plot_results(labels, normalize, sparse_cutoff, axis, style, error_bars, *results)
    151 all_items = lambda: chain.from_iterable(r.items() for r in group.values())
    153 # create axis labels
--> 154 size = dim**n_sys
    155 if type(sparse_cutoff) is not bool:
    156     cutoff = sparse_cutoff

TypeError: unsupported operand type(s) for ** or pow(): 'NoneType' and 'int'
../../_images/guides_run_qiskit_submission_example_8_2.png

Submitting a Circuit Collection

To submit a CircuitCollection we can make use of the batch() method (see also Example: Circuit Batching), as the following example demonstrates:

[6]:
# Define a 3-qubit cycle to work with.
cycle = {0: tq.Gate.x, 1: tq.Gate.y, 2: tq.Gate.h}

# Generate a circuit collection to measure noise.
circuits = tq.make_knr(cycle, [4, 32, 64], 24)

The executor (defined below) will automatically attempt to batch the circuit collection into the maximum number of circuits per job that the backend supports. Here, however, we manually batch beforehand. Supposing the backend accepts at most 75 circuits and has a memory cutoff for the number of gates allowed per job, we choose to riffle circuits in the batch by circuit depth. In our protocol above, we selected 3 sequence lengths, 4, 32, and 64, with 24 random circuits per sequence length per configuration. Thus we use fit \(24\times 3+2=72\) circuits per batch, where the extra \(2\) are readout calibration (RCAL) circuits.

[7]:
ro_circuits = tq.make_rcal(circuits.labels)
batches = circuits.batch(74, extra_circuits=ro_circuits, sequencer=tq.sequencer.RIFFLER)

Run the batches on our backend. If a filename is provided, it will periodically save to the given file so that we can resume the experiment if, for example, our Python kernel crashes.

[8]:
ex = tq.interface.Executor(batches, backend, n_shots=128)

# the executor is asynchronous, call a blocking function to wait for it to finish
ex.block()

circuits.plot.timestamps()
True-Q formatting will not be loaded without trusting this notebook or rerunning the affected cells. Notebooks can be marked as trusted by clicking "File -> Trust Notebook".
../../_images/guides_run_qiskit_submission_example_14_6.png

Note

When running in Jupyter, the executor has an automatically updating output which relies on IPywidgets being installed and enabled. If these are not installed then no display will show up when running the executor in Jupyter.

Transpiling for a Specific Backend

Sometimes it is useful to see what the circuit conversion is doing for a particular circuit. To do this, we first instantiate a True-Q configuration object from our desired backend. This will contain the device topology and native gates of the backend. We create a compiler object based on this configuration.

Note

This process is done during submission automatically by the Executor, and the steps below should only be used as a reference. The output of this should not be put into the Executor or it will apply the same compiler operations a second time, which may alter the circuit further.

[9]:
config = tq.interface.qiskit.config_from_backend(backend)
transpiler = tq.Compiler.from_config(config)

Define a circuit.

[10]:
circuit = tq.Circuit([{4: tq.Gate.random(2), 5: tq.Gate.x}])
circuit
[10]:
True-Q formatting will not be loaded without trusting this notebook or rerunning the affected cells. Notebooks can be marked as trusted by clicking "File -> Trust Notebook".
Circuit
Key:
No key present in circuit.
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(4): Gate(Y, X, ...)
Name:
  • Gate(Y, X, ...)
Generators:
  • 'Y': 88.064
  • 'X': -93.711
  • 'Z': 47.187
Matrix:
  • 0.10 -0.48j -0.09 0.87j 0.86 0.15j 0.49 0.03j
(5): Gate.x
Name:
  • Gate.x
Aliases:
  • Gate.x
  • Gate.cliff1
Generators:
  • 'X': 180.0
Matrix:
  • 1.00 1.00

Transpile the circuit based on the device.

[11]:
transpiled_circuit = transpiler.compile(circuit)
transpiled_circuit
[11]:
True-Q formatting will not be loaded without trusting this notebook or rerunning the affected cells. Notebooks can be marked as trusted by clicking "File -> Trust Notebook".
Circuit
Key:
No key present in circuit.
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(4): RZGate(phi)
Name:
  • RZGate(phi)
Parameters:
  • phi = -3.239742
Generators:
  • 'Z': -185.624
Matrix:
  • -0.05 1.00j -0.05 -1.00j
(5): RZGate(phi)
Name:
  • RZGate(phi)
Aliases:
  • Gate.cliff8
Parameters:
  • phi = -1.570796
Generators:
  • 'Z': -90.0
Matrix:
  • 0.71 0.71j 0.71 -0.71j
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(4): SXGate()
Name:
  • SXGate()
Aliases:
  • Gate.sx
  • Gate.cliff5
Generators:
  • 'X': 90.0
Matrix:
  • 0.50 0.50j 0.50 -0.50j 0.50 -0.50j 0.50 0.50j
(5): SXGate()
Name:
  • SXGate()
Aliases:
  • Gate.sx
  • Gate.cliff5
Generators:
  • 'X': 90.0
Matrix:
  • 0.50 0.50j 0.50 -0.50j 0.50 -0.50j 0.50 0.50j
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(4): RZGate(phi)
Name:
  • RZGate(phi)
Parameters:
  • phi = 1.017262
Generators:
  • 'Z': 58.285
Matrix:
  • 0.87 -0.49j 0.87 0.49j
(5): RZGate(phi)
Name:
  • RZGate(phi)
Aliases:
  • Gate.id
  • Gate.i
  • Gate.cliff0
Likeness:
  • Identity
Parameters:
  • phi = 0.0
Generators:
  • 'I': 0
Matrix:
  • 1.00 1.00
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(4): SXGate()
Name:
  • SXGate()
Aliases:
  • Gate.sx
  • Gate.cliff5
Generators:
  • 'X': 90.0
Matrix:
  • 0.50 0.50j 0.50 -0.50j 0.50 -0.50j 0.50 0.50j
(5): SXGate()
Name:
  • SXGate()
Aliases:
  • Gate.sx
  • Gate.cliff5
Generators:
  • 'X': 90.0
Matrix:
  • 0.50 0.50j 0.50 -0.50j 0.50 -0.50j 0.50 0.50j
 
Marker 0
Compilation tools may only recompile cycles with equal markers.
(4): RZGate(phi)
Name:
  • RZGate(phi)
Parameters:
  • phi = 1.534761
Generators:
  • 'Z': 87.935
Matrix:
  • 0.72 -0.69j 0.72 0.69j
(5): RZGate(phi)
Name:
  • RZGate(phi)
Aliases:
  • Gate.cliff8
Parameters:
  • phi = -1.570796
Generators:
  • 'Z': -90.0
Matrix:
  • 0.71 0.71j 0.71 -0.71j

Footnotes


Download

Download this file as Jupyter notebook: qiskit_submission_example.ipynb.