| author | azure-quantum-content | ||||
|---|---|---|---|---|---|
| description | Learn how to submit specific formatted quantum circuits with QIR, OpenQASM, or Pulser SDK to the Azure Quantum service. | ||||
| ms.author | quantumdocwriters | ||||
| ms.date | 03/23/2026 | ||||
| ms.service | azure-quantum | ||||
| ms.subservice | qdk | ||||
| ms.topic | how-to | ||||
| no-loc |
|
||||
| title | Submit formatted quantum circuits | ||||
| uid | microsoft.quantum.quickstarts.computing.provider |
Learn how to use the qdk.azure Python module to submit circuits in specific formats to the Azure Quantum service. This article shows you how to submit circuits in the following formats:
For more information, see Quantum circuits.
To develop and run your circuits in Visual Studio Code (VS Code), you must have the following:
-
An Azure account with an active subscription. If you don’t have an Azure account, register for free and sign up for a pay-as-you-go subscription.
-
An Azure Quantum workspace. For more information, see Create an Azure Quantum workspace.
-
A Python environment with Python and Pip installed.
-
VS Code with the Microsoft Quantum Development Kit (QDK), Python, and Jupyter extensions installed.
-
The
qdkPython library with theazureextra, and theipykernelpackage.python -m pip install --upgrade "qdk[azure]" ipykernel
To connect to your workspace in a Jupyter notebook in VS Code, follow these steps:
-
In VS Code, open the View menu and choose Command Palette.
-
Enter Create: New Jupyter Notebook. An empty Jupyter Notebook file opens in a new tab.
-
In the first cell of the notebook, run the following code. You can find the resource ID in the Overview pane for your workspace in the Azure portal.
from qdk.azure import Workspace workspace = Workspace (resource_id="") # Add your resource ID
Quantum Intermediate Representation (QIR) is an intermediate representation that serves as a common interface between quantum programming languages and targeted quantum computation platforms. For more information, see Quantum Intermediate Representation.
To submit a QIR-formatted circuit, follow these steps:
-
Create the QIR circuit. For example, run the following code in a new cell to create a simple entanglement circuit.
QIR_routine = """%Result = type opaque %Qubit = type opaque define void @ENTRYPOINT__main() #0 { call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*)) call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 call void @__quantum__rt__tuple_record_output(i64 2, i8* null) call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) ret void } declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*) declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*) declare void @__quantum__qis__cy__body(%Qubit*, %Qubit*) declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*) declare void @__quantum__qis__rx__body(double, %Qubit*) declare void @__quantum__qis__rxx__body(double, %Qubit*, %Qubit*) declare void @__quantum__qis__ry__body(double, %Qubit*) declare void @__quantum__qis__ryy__body(double, %Qubit*, %Qubit*) declare void @__quantum__qis__rz__body(double, %Qubit*) declare void @__quantum__qis__rzz__body(double, %Qubit*, %Qubit*) declare void @__quantum__qis__h__body(%Qubit*) declare void @__quantum__qis__s__body(%Qubit*) declare void @__quantum__qis__s__adj(%Qubit*) declare void @__quantum__qis__t__body(%Qubit*) declare void @__quantum__qis__t__adj(%Qubit*) declare void @__quantum__qis__x__body(%Qubit*) declare void @__quantum__qis__y__body(%Qubit*) declare void @__quantum__qis__z__body(%Qubit*) declare void @__quantum__qis__swap__body(%Qubit*, %Qubit*) declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare void @__quantum__rt__result_record_output(%Result*, i8*) declare void @__quantum__rt__array_record_output(i64, i8*) declare void @__quantum__rt__tuple_record_output(i64, i8*) attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="4" "required_num_results"="2" } attributes #1 = { "irreversible" } ; module flags !llvm.module.flags = !{!0, !1, !2, !3} !0 = !{i32 1, !"qir_major_version", i32 1} !1 = !{i32 7, !"qir_minor_version", i32 0} !2 = !{i32 1, !"dynamic_qubit_management", i1 false} !3 = !{i32 1, !"dynamic_result_management", i1 false} """
-
Create a
submit_qir_jobhelper function to submit the QIR circuit to a target. In this example, the input and output data formats areqir.v1andmicrosoft.quantum-results.v1, respectively.# Submit the job with proper input and output data formats def submit_qir_job(target, input, name, count=100): job = target.submit( input_data=input, input_data_format="qir.v1", output_data_format="microsoft.quantum-results.v1", name=name, input_params = { "entryPoint": "ENTRYPOINT__main", "arguments": [], "count": count } ) print(f"Queued job: {job.id}") job.wait_until_completed() print(f"Job completed with state: {job.details.status}") #if job.details.status == "Succeeded": result = job.get_results() return result
-
Submit the QIR circuit to a specific Azure Quantum target. For example, to submit the QIR circuit to the IonQ simulator target, run the following code:
target = workspace.get_targets(name="ionq.simulator") result = submit_qir_job(target, QIR_routine, "QIR routine") result
Each Azure Quantum provider has its own format to represent quantum circuits. You can submit circuits to Azure Quantum in provider-specific formats instead of QIR languages, such as Q# or Qiskit.
IonQ uses JSON format to run circuits on their targets. For more information, see IonQ targets and the IonQ API documentation.
The following sample creates a superposition between three qubits in JSON format.
-
In a new cell, create a quantum circuit in JSON format.
circuit = { "qubits": 3, "circuit": [ { "gate": "h", "target": 0 }, { "gate": "cnot", "control": 0, "target": 1 }, { "gate": "cnot", "control": 0, "target": 2 }, ] }
-
Submit the circuit to the IonQ target. The following example uses the IonQ simulator, which returns a
Jobobject.target = workspace.get_targets(name="ionq.simulator") job = target.submit(circuit)
-
When the job completes, get the results.
results = job.get_results() print(results)
You can use the Pulser SDK to create pulse sequences and submit them to Pasqal targets.
Pulser is a framework that allows you to create, simulate, and run pulse sequences for neutral-atom quantum devices. Pulser is designed by PASQAL as a pass-through to submit quantum experiments to their quantum processors. For more information, see the Pulser documentation.
To submit the pulse sequences, first install the Pulser SDK packages:
try:
import pulser
import pulser_azure
except ImportError:
!pip -q install pulser pulser-azure --index-url https://pypi.org/simpleDefine both a register and a layout. The register specifies where to arrange the atoms, and the layout specifies the positions of traps that capture and structure the atoms within the register.
For details on layouts, see the Pulser documentation.
Create a devices object to import the Pasqal quantum computer target, FRESNEL_CAN1.
from pulser_azure import AzureConnection
connection = AzureConnection(resource_id="") # Add your resource ID
devices = connection.fetch_available_devices()
QPU = devices["pasqal.qpu.fresnel-can1"]Use a custom layout when the pre-calibrated layouts don't satisfy the requirements of your experiment.
For a given arbitrary register, a neutral-atom QPU places traps according to the layout, which must then be calibrated. Because each calibration takes time, it's a best practice to reuse an existing calibrated layout when possible.
To create an arbitrary layout, choose one of the following options:
-
Automatically generate a layout based on a specified register. For large registers, this process can produce sub-optimal solutions. For example:
from pulser import Register qubits = { "q0": (0, 0), "q1": (0, 10), "q2": (8, 2), "q3": (1, 15), "q4": (-10, -3), "q5": (-8, 5), } reg = Register(qubits).with_automatic_layout(device)
-
To manually define a layout to create your register, see the Pulser documentation.
Neutral atoms are controlled with laser pulses. The Pulser SDK allows you to create pulse sequences to apply to the quantum register.
-
Define the pulse sequence attributes by declaring the channels that control the atoms. To create a
Sequence, provide aRegisterinstance along with the device where the sequence will be executed. For example, the following code declares one channel:ch0.from pulser import Sequence seq = Sequence(reg, QPU) # Print the available channels for your sequence print(seq.available_channels) # Declare a channel. For example, `rydberg_global` seq.declare_channel("ch0", "rydberg_global")
[!NOTE] You can use the
QPU = devices["pasqal.qpu.fresnel-can1"]device or import a virtual device from Pulser for more flexibility. The use of aVirtualDeviceallows for sequence creation that's less constrained by device specifications, which lets you run on an emulator. For more information, see Pulser documentation. -
Add pulses to your sequence. To do so, create and add pulses to the channels that you declared. For example, the following code creates a pulse and adds it to channel
ch0:from pulser import Pulse from pulser.waveforms import RampWaveform, BlackmanWaveform import numpy as np amp_wf = BlackmanWaveform(1000, np.pi) det_wf = RampWaveform(1000, -5, 5) pulse = Pulse(amp_wf, det_wf, 0) seq.add(pulse, "ch0") seq.draw()
The following image shows the pulse sequence:
:::image type="content" source="media/provider-format-pasqal-sequence.png" alt-text="Pulse sequence":::
To submit the pulse sequences, convert the Pulser objects into a JSON string that can be used as input data.
import json
# Convert the sequence to a JSON string
def prepare_input_data(seq):
input_data = {}
input_data["sequence_builder"] = json.loads(seq.to_abstract_repr())
to_send = json.dumps(input_data)
return to_send-
Set the proper input and output data formats. For example, the following code sets the input data format to
pasqal.pulser.v1and the output data format topasqal.pulser-results.v1.# Submit the job with proper input and output data formats def submit_job(target, seq, shots): job = target.submit( input_data=prepare_input_data(seq), # Take the JSON string previously defined as input data input_data_format="pasqal.pulser.v1", output_data_format="pasqal.pulser-results.v1", name="Pasqal sequence", shots=shots # Number of shots ) print(f"Queued job: {job.id}") return job
[!NOTE] The time required to run a job on the QPU depends on current queue times. You can view the average queue time for a target in the Providers pane of your workspace.
-
Submit the program to Pasqal. Before you submit your code to real quantum hardware, it's a best practice to test your code on the emulator
pasqal.sim.emu-mpstarget.target = workspace.get_targets(name="pasqal.sim.emu-mps") # Change to "pasqal.qpu.fresnel-can1" to use FRESNEL_CAN1 QPU job = submit_job(target, seq, 10) job.wait_until_completed() print(f"Job completed with state: {job.details.status}") result = job.get_results() print(result)
{ "1000000": 8, "0010000": 1, "0010101": 1 }
With AzureConnection from pulser-azure, you can directly use a QPU backend or a remote emulator backend to execute the sequence.
from pulser.backends import RemoteMPSBackend, QPUBackend
backend = RemoteMPSBackend(sequence=seq, connection=connection) # Replace RemoteMPSBackend with QPUBackend to execute the sequence on the QPU
results = backend.run(job_params=[{"runs": 10}], wait=True)
print(results.results[0].final_bitstrings)Counter({"1000000": 8, "0010000": 1, "0010101": 1})
-
Create a quantum circuit in the OpenQASM representation. For example, the following code creates a Teleportation circuit:
circuit = """OPENQASM 2.0; include "qelib1.inc"; qreg q[3]; creg c0[3]; h q[0]; cx q[0], q[1]; cx q[1], q[2]; measure q[0] -> c0[0]; measure q[1] -> c0[1]; measure q[2] -> c0[2]; """
Or, load the circuit from an OpenQASM file:
with open("my_teleport.qasm", "r") as f: circuit = f.read()
-
Submit the circuit to a Quantinuum target. The following example submits the job to one of the Quantinuum simulator targets.
target = workspace.get_targets(name="quantinuum.sim.h2-1sc") job = target.submit(circuit, shots=500)
-
Wait until the job is complete and then fetch the results.
results = job.get_results() print(results)
Note
These results return 000 for every shot, which isn't random. This is because the API Validator only checks whether your code can run on Quantinuum hardware, but returns 0 for every quantum measurement. For a true random number generator, you need to run your circuit on quantum hardware.
To submit a Quil job to a Rigetti target, use the qdk.azure Python module.
-
Load the required imports.
from azure.quantum import Workspace from azure.quantum.target.rigetti import Result, Rigetti, RigettiTarget, InputParams
-
Create a
targetobject and pass the name of the Rigetti target that you want to submit your job to. For example, the following code selects theQVMtarget.target = Rigetti(workspace=workspace, name=RigettiTarget.QVM)
-
Create a Quil program. For your program to be accepted, you must set the readout to
"ro".readout = "ro" bell_state_quil = f""" DECLARE {readout} BIT[2] H 0 CNOT 0 1 MEASURE 0 {readout}[0] MEASURE 1 {readout}[1] """ num_shots = 5 job = target.submit( input_data=bell_state_quil, name="bell state", shots=100, input_params=InputParams(skip_quilc=False) ) print(f"Job completed with state: {job.details.status}") result = Result(job) # This throws an exception if the job failed
-
You can index a Result with the name of the readout. In the following code,
data_per_shotis a list of lengthnum_shots, and each item in the list is another list that contains the data for the register from that shot.data_per_shot = result[readout] ro_data_first_shot = data_per_shot[0]
In this case, because the type of the register is BIT, the type is integer and the value either 0 or 1.
assert isinstance(ro_data_first_shot[0], int) assert ro_data_first_shot[0] == 1 or ro_data_first_shot[0] == 0
-
Print out all the data.
print(f"Data from '{readout}' register:") for i, shot in enumerate(data_per_shot): print(f"Shot {i}: {shot}")
Important
You can't submit multiple circuits on a single job. As a workaround you can call the backend.run method to submit each circuit asynchronously, and then fetch the results of each job. For example:
jobs = []
for circuit in circuits:
jobs.append(backend.run(circuit, shots=N))
results = []
for job in jobs:
results.append(job.result())- Submit a circuit with Qiskit to Azure Quantum.
- Submit a circuit with Cirq to Azure Quantum.