Quick Start#
IQM Pulla is a client-side software which allows the user to control the generation and execution of pulse-level instruction schedules on a quantum computer. Within the existing IQM QCCSW stack, Pulla is somewhere between circuit-level execution and EXA-experiment. Namely, with pulse-level access the user can:
compile a quantum circuit (e.g. a Qiskit circuit) into an instruction schedule on the client side
access and modify the calibration data to be used for the circuit-to-schedule compilation
view and modify the default implementations of quantum gates
define custom implementations of quantum gates
define new composite gates out of native gates and set their calibration data
control the multi-step compilation procedure, and edit the intermediate data
use custom pulse shapes
This notebook contains a small meaningful example for a “quick start”. Refer to other chapters for more details on various aspects of Pulla.
Authentication#
IQM uses bearer token authentication to manage access to quantum computers.
Get your personal token from the web dashboard and set the environment variable IQM_TOKEN accordingly in your Jupyter notebook:
import os
os.environ["IQM_TOKEN"]="<YOUR TOKEN HERE>"
p = Pulla(iqm_server_url)
Setting IQM_TOKEN env. variable enables authentication for IQM Client as well, so your Qiskit runs against the same station will work, too.
Alternatively, the token can be provided as argument token to :class:.Pulla constructor:
p = Pulla(iqm_server_url, token="<YOUR TOKEN HERE>")
import os
from qiskit import QuantumCircuit, visualization
from qiskit.compiler import transpile
from iqm.qiskit_iqm import IQMProvider
from iqm.qiskit_iqm.iqm_transpilation import optimize_single_qubit_gates
from iqm.pulla.pulla import Pulla
from iqm.pulla.utils_qiskit import qiskit_to_pulla, sweep_job_to_qiskit
# Create a Pulla object and a qiskit-iqm backend for accessing the quantum computer.
iqm_server_url = os.environ['PULLA_IQM_SERVER_URL'] # or set the URL directly here
os.environ["IQM_TOKEN"] = os.environ.get("IQM_TOKEN") # or set the token directly here
p = Pulla(iqm_server_url)
provider = IQMProvider(iqm_server_url)
backend = provider.get_backend()
shots = 100
# Define a quantum circuit.
qc = QuantumCircuit(3, 3)
qc.h(0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.measure_all()
qc.draw(output='mpl')
qc_transpiled = transpile(qc, backend=backend, layout_method='sabre', optimization_level=3)
qc_optimized = optimize_single_qubit_gates(qc_transpiled)
qc_optimized.draw(output="mpl")
# Transpile the circuit using Qiskit, and then convert it into Pulla format.
qc_transpiled = transpile(qc, backend=backend, layout_method='sabre', optimization_level=3)
qc_optimized = optimize_single_qubit_gates(qc_transpiled)
circuits, compiler = qiskit_to_pulla(p, backend, qc_optimized)
# Compile the circuit into an instruction schedule playlist.
job_definition, context = compiler.compile(circuits)
# Pulla.submit_playlist() returns a SweepJob object.
# The measurements are obtained using SweepJob.result() after SweepJob.wait_for_completion().
job = p.submit_playlist(job_definition, context=context)
job.wait_for_completion()
qiskit_result = sweep_job_to_qiskit(job, shots=shots)
print(f"Raw results:\n{job.result()}\n")
print(f"Qiskit result counts:\n{qiskit_result.get_counts()}\n")
visualization.plot_histogram(qiskit_result.get_counts())
# The results should be comparable to a direct circuit execution through IQM Server.
print("Executing the same circuit via IQM Server...")
job = backend.run(qc_optimized, shots=shots)
print(f"Qiskit result counts from IQM Server:\n{job.result().get_counts()}")
visualization.plot_histogram(job.result().get_counts())
Using Pulla with IQM Resonance#
Pulla can also be used with IQM Resonance cloud service. You need to set the IQM Server URL to point to Resonance and specify the quantum computer to use.
iqm_server_url = os.environ.get("RESONANCE_URL") # typically "https://resonance.meetiqm.com"
# The names of the available quantum computers can be found on the Resonance dashboard.
quantum_computer = os.environ.get("RESONANCE_QC_NAME")
os.environ["IQM_TOKEN"] = os.environ.get("RESONANCE_API_TOKEN") # or set the token directly here
p = Pulla(iqm_server_url, quantum_computer=quantum_computer)
provider = IQMProvider(iqm_server_url, quantum_computer=quantum_computer)
backend = provider.get_backend(use_metrics=False)
# From now on, the usage is same as for non-Resonance usage
# qc = ...
qc_transpiled = transpile(qc, backend=backend, layout_method='sabre', optimization_level=3)
qc_optimized = optimize_single_qubit_gates(qc_transpiled)
circuits, compiler = qiskit_to_pulla(p, backend, qc_optimized)
pl, context = compiler.compile(circuits)
job = p.submit_playlist(pl, context=context)
job.wait_for_completion()
qiskit_result = sweep_job_to_qiskit(job, shots=shots)
print(f"Qiskit result counts:\n{qiskit_result.get_counts()}\n")
Schedule visualization#
IQM Pulse comes with a schedule visualizer. It takes a Playlist (i.e. a compressed list of instruction schedules) and a list of schedule/segment indices to inspect.
The playlist variable below is the one which we derived from the original Qiskit circuit using the Pulla compiler, and it only has one schedule.
from iqm.pulse.playlist.visualisation.base import inspect_playlist
from IPython.core.display import HTML
HTML(inspect_playlist(job_definition.sweep_definition.playlist, [0]))