Submitting Jobs with Pulse-Level Access

This guide demonstrates how to submit quantum computing jobs with pulse-level access using the IQM Pulla library.

Overview

What this pulse-level access example do:

  • We define quantum circuits at the gate level using Qiskit
  • The circuit is transpiled and converted to pulse schedules
  • Jobs are executed using the Pulla client

Requirement

This notebook requires a Python virtual environment with the following additional libraries installed

iqm-pulla==5.28

Step 1: Import Python library dependencies

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, station_control_result_to_qiskit
from iqm.iqm_client import IQMClient

Step 2: Initialize the pulse-level client

Initialize both the Pulla client for pulse-level execution and the IQM client for retrieving results.

station_url = ""  # TODO: Change to your station URL if needed
server_url = ""  # TODO: Change to your server URL if needed

api_token = (
    ""  # TODO: Insert your API token here or set the IQM_API_TOKEN environment variable
)

# Initialize Pulla client for pulse-level execution
p = Pulla(station_url, get_token_callback=lambda: f"Bearer {api_token}")
provider = IQMProvider(server_url, token=api_token)
backend = provider.get_backend()

# Initialize IQM client for fetching results
client = IQMClient(url=server_url, token=api_token)

Step 3: Define the quantum circuit to be executed

Create a quantum circuit using Qiskit. This example creates a simple entanglement circuit.

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")

Step 4: Transpile quantum circuit to pulse schedule

Convert the quantum circuit to pulse-level instructions that can be executed on the quantum hardware.

# 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.
playlist, context = compiler.compile(circuits)
settings, context = compiler.build_settings(context, shots=shots)
# Optionally visualize the pulse playlist
from iqm.pulse.playlist.visualisation.base import inspect_playlist
from IPython.core.display import HTML

HTML(inspect_playlist(playlist, [0]))

Step 5: Execute the job and retrieve results

Execute the pulse schedule on the quantum hardware.

# Execute the job
# Pulla.execute() returns a StationControlResult object; the measurements are in StationControlResult.result
# By default execute() prints the measurement results; disable it with verbose=False
response_data = p.execute(playlist, context, settings, verbose=False)
qiskit_result = station_control_result_to_qiskit(
    response_data, shots=shots, execution_options=context["options"]
)

print(f"Raw results:\n{response_data.result}\n")
print(f"Qiskit result counts:\n{qiskit_result.get_counts()}\n")
visualization.plot_histogram(qiskit_result.get_counts())