|
7 | 7 | "source": [ |
8 | 8 | "# Submitting Qiskit Circuits to Azure Quantum with the QDK\n", |
9 | 9 | "\n", |
10 | | - "This notebook shows how to take an arbitrary Qiskit `QuantumCircuit`, compile it to QIR using the Quantum Development Kit (QDK) Python APIs, and submit it as a job to an Azure Quantum target. It also demonstrates how to handle parameterized circuits by binding parameters before submission." |
| 10 | + "This notebook demonstrates two ways to run Qiskit `QuantumCircuit` jobs on Azure Quantum using the QDK: (A) direct submission via `AzureQuantumProvider` for a streamlined workflow, and (B) an explicit OpenQASM 3 → QIR compilation path for transparency and artifact inspection. It also shows how to handle parameterized circuits by binding parameters prior to execution." |
11 | 11 | ] |
12 | 12 | }, |
13 | 13 | { |
14 | 14 | "cell_type": "markdown", |
15 | 15 | "id": "2b838c23", |
16 | 16 | "metadata": {}, |
17 | 17 | "source": [ |
18 | | - "The workflow demonstrated here:\n", |
| 18 | + "This workflow demonstrates two methods of submission:\n", |
19 | 19 | "\n", |
| 20 | + "### A. Direct submission via `AzureQuantumProvider` (recommended when available)\n", |
| 21 | + "1. Build (or load) a Qiskit `QuantumCircuit`.\n", |
| 22 | + "2. Reference an existing Azure Quantum workspace with `qdk.azure.Workspace`.\n", |
| 23 | + "3. Instantiate `AzureQuantumProvider(workspace)` and pick a backend (`provider.get_backend(<target_name>)`).\n", |
| 24 | + "4. Call `backend.run(circuit, shots, job_name=...)` and fetch results (`job.result().get_counts(circuit)`).\n", |
| 25 | + "\n", |
| 26 | + "### B. Submit via OpenQASM compilation (explicit OpenQASM → QIR → submit)\n", |
20 | 27 | "1. Build (or load) a Qiskit `QuantumCircuit`.\n", |
21 | 28 | "2. Convert it to OpenQASM 3 text (`qiskit.qasm3.dumps`).\n", |
22 | | - "3. Compile that OpenQASM 3 source to QIR with `qdk.openqasm.compile`.\n", |
23 | | - "4. Create (or attach to) an Azure Quantum workspace with `qdk.azure.Workspace`.\n", |
| 29 | + "3. Compile the OpenQASM 3 source to QIR with `qdk.openqasm.compile` (choose a `TargetProfile`).\n", |
| 30 | + "4. Reference an existing Azure Quantum workspace with `qdk.azure.Workspace`.\n", |
24 | 31 | "5. Select a target (e.g. a simulator such as `rigetti.sim.qvm`).\n", |
25 | | - "6. Submit the QIR payload and retrieve measurement results.\n", |
26 | | - "\n", |
27 | | - "Why use the QDK in this flow?\n", |
28 | | - "\n", |
29 | | - "- Local compilation: QIR generation happens locally—no external CLI needed.\n", |
30 | | - "- Consistency: The same compiler stack powers Q#, OpenQASM, and (via translation) Qiskit interoperability.\n", |
31 | | - "\n", |
32 | | - "This notebook focuses strictly on submission. For local resource estimation of Qiskit circuits, see the separate estimation sample." |
| 32 | + "6. Submit the QIR payload (`target.submit(qir, job_name, shots)`), then retrieve results (`job.get_results()`)." |
33 | 33 | ] |
34 | 34 | }, |
35 | 35 | { |
|
128 | 128 | "location = 'westus'" |
129 | 129 | ] |
130 | 130 | }, |
| 131 | + { |
| 132 | + "cell_type": "markdown", |
| 133 | + "id": "cdae59f7", |
| 134 | + "metadata": {}, |
| 135 | + "source": [ |
| 136 | + "### Approach A: Using Azure Quantum Provider\n", |
| 137 | + "\n", |
| 138 | + "Here we use the recommended submission method with `AzureQuantumProvider` to submit the Qiskit circuit straight to an Azure Quantum backend. This avoids having to explicitly do QASM translation or QIR compilation steps." |
| 139 | + ] |
| 140 | + }, |
131 | 141 | { |
132 | 142 | "cell_type": "code", |
133 | 143 | "execution_count": null, |
134 | 144 | "id": "d9a0f59b", |
135 | 145 | "metadata": {}, |
136 | 146 | "outputs": [], |
| 147 | + "source": [ |
| 148 | + "from qdk.azure import Workspace\n", |
| 149 | + "from qdk.azure.qiskit import AzureQuantumProvider\n", |
| 150 | + "\n", |
| 151 | + "def submit_qiskit_circuit_to_azure_provider(circuit, target_name, name, shots=100):\n", |
| 152 | + "\n", |
| 153 | + " workspace = Workspace(\n", |
| 154 | + " subscription_id=subscription_id,\n", |
| 155 | + " resource_group=resource_group,\n", |
| 156 | + " name=workspace_name,\n", |
| 157 | + " location=location,\n", |
| 158 | + " )\n", |
| 159 | + "\n", |
| 160 | + " provider = AzureQuantumProvider(workspace)\n", |
| 161 | + " backend = provider.get_backend(target_name)\n", |
| 162 | + " job = backend.run(circuit, shots, job_name=name)\n", |
| 163 | + " return job.result().get_counts(circuit)\n", |
| 164 | + "\n", |
| 165 | + "provider_counts = submit_qiskit_circuit_to_azure_provider(circuit, \"rigetti.sim.qvm\", \"qiskit-provider-job\")\n", |
| 166 | + "print(provider_counts)" |
| 167 | + ] |
| 168 | + }, |
| 169 | + { |
| 170 | + "cell_type": "markdown", |
| 171 | + "id": "fbf60412", |
| 172 | + "metadata": {}, |
| 173 | + "source": [ |
| 174 | + "### Approach B: Submit via OpenQASM compilation\n", |
| 175 | + "\n", |
| 176 | + "Below we show the longer path that exposes intermediate artifacts. This is useful if you want to inspect or transform OpenQASM/QIR, or integrate with tooling that consumes QIR directly.\n" |
| 177 | + ] |
| 178 | + }, |
| 179 | + { |
| 180 | + "cell_type": "code", |
| 181 | + "execution_count": null, |
| 182 | + "id": "ee47d6f6", |
| 183 | + "metadata": {}, |
| 184 | + "outputs": [], |
137 | 185 | "source": [ |
138 | 186 | "from qiskit import qasm3\n", |
139 | 187 | "from qdk.openqasm import compile\n", |
140 | | - "from qdk.azure import Workspace\n", |
141 | 188 | "from qdk import TargetProfile\n", |
142 | 189 | "\n", |
143 | | - "def submit_qiskit_circuit_to_azure(circuit, target_name, name, shots=100):\n", |
| 190 | + "def submit_qiskit_circuit_to_azure_via_qasm(circuit, target_name, name, shots=100):\n", |
144 | 191 | " qasm3_str = qasm3.dumps(circuit)\n", |
145 | 192 | " qir = compile(qasm3_str, target_profile=TargetProfile.Base)\n", |
146 | 193 | "\n", |
|
154 | 201 | " job = target.submit(qir, name, shots=shots)\n", |
155 | 202 | " return job.get_results()\n", |
156 | 203 | "\n", |
157 | | - "results = submit_qiskit_circuit_to_azure(circuit, \"rigetti.sim.qvm\", \"qiskit-job\")\n", |
| 204 | + "results = submit_qiskit_circuit_to_azure_via_qasm(circuit, \"rigetti.sim.qvm\", \"qiskit-via-qasm-job\")\n", |
158 | 205 | "print(results)" |
159 | 206 | ] |
160 | 207 | }, |
|
165 | 212 | "source": [ |
166 | 213 | "## Parameterized Qiskit circuits\n", |
167 | 214 | "\n", |
168 | | - "Many algorithms use symbolic parameters. Before compiling to QIR and submitting, all circuit parameters must be bound to numeric values. Below we build a simple entangling ladder, apply a rotation parameterized by θ across all qubits, then uncompute the entanglement and measure the first qubit." |
| 215 | + "Many algorithms use symbolic parameters. Before submitting to azure (or before compiling to QIR), all circuit parameters must be bound to numeric values. Below we build a simple entangling ladder, apply a rotation parameterized by `θ` across all qubits, then uncompute the entanglement and measure the first qubit." |
169 | 216 | ] |
170 | 217 | }, |
171 | 218 | { |
|
178 | 225 | "from qiskit import QuantumCircuit\n", |
179 | 226 | "from qiskit.circuit import Parameter\n", |
180 | 227 | "\n", |
181 | | - "\n", |
182 | 228 | "def get_parameterized_circuit(n = 5) -> QuantumCircuit:\n", |
183 | 229 | " theta = Parameter(\"θ\")\n", |
184 | 230 | " qc = QuantumCircuit(n, 1)\n", |
|
194 | 240 | " return qc\n", |
195 | 241 | "\n", |
196 | 242 | "# Build the symbolic (parameterized) circuit\n", |
197 | | - "circuit = get_parameterized_circuit()\n", |
| 243 | + "parameterized_circuit = get_parameterized_circuit()\n", |
198 | 244 | "\n", |
199 | 245 | "# Bind θ to a numeric value, then visualize the bound circuit\n", |
200 | | - "circuit.assign_parameters({\"θ\": 0.5}).draw(output=\"text\")" |
| 246 | + "bound_circuit = parameterized_circuit.assign_parameters({\"θ\": 0.5})\n", |
| 247 | + "\n", |
| 248 | + "bound_circuit.draw(output=\"text\")" |
201 | 249 | ] |
202 | 250 | }, |
203 | 251 | { |
|
207 | 255 | "source": [ |
208 | 256 | "## Submitting a bound parameterized circuit\n", |
209 | 257 | "\n", |
210 | | - "Below we reuse the parameterized circuit from above, bind θ to 0.5, and submit the resulting (fully concrete) circuit to the Azure Quantum target using the same helper function as before. The printed result shows the measurement counts. Change the value of θ (or loop over several values) to explore how the outcome distribution varies." |
| 258 | + "Here we submit the `bound_circuit` from above using the recommended `AzureQuantumProvider` approach. The printed result shows the measurement counts. Change the value of `θ` (or loop over several values) to explore how the outcome distribution varies." |
211 | 259 | ] |
212 | 260 | }, |
213 | 261 | { |
|
217 | 265 | "metadata": {}, |
218 | 266 | "outputs": [], |
219 | 267 | "source": [ |
220 | | - "# Reuse the previously defined parameterized circuit\n", |
221 | | - "# (circuit is symbolic and must have θ bound to a value before submission)\n", |
222 | | - "\n", |
223 | | - "results = submit_qiskit_circuit_to_azure(\n", |
224 | | - " circuit.assign_parameters({\"θ\": 0.5}),\n", |
| 268 | + "results = submit_qiskit_circuit_to_azure_provider(\n", |
| 269 | + " bound_circuit,\n", |
225 | 270 | " \"rigetti.sim.qvm\",\n", |
226 | 271 | " \"qiskit-parameterized-job\"\n", |
227 | 272 | ")\n", |
|
245 | 290 | "name": "python", |
246 | 291 | "nbconvert_exporter": "python", |
247 | 292 | "pygments_lexer": "ipython3", |
248 | | - "version": "3.13.8" |
| 293 | + "version": "3.13.9" |
249 | 294 | } |
250 | 295 | }, |
251 | 296 | "nbformat": 4, |
|
0 commit comments