microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/fix-wasm-logging-issue

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

samples/python_interop/cirq_submission_to_azure.ipynb

224lines · modecode

1{
2 "cells": [
3 {
4 "cell_type": "markdown",
5 "id": "fae381bf",
6 "metadata": {},
7 "source": [
8 "# Submitting Cirq Circuits to Azure Quantum with the QDK\n",
9 "\n",
10 "This notebook demonstrates how to run Cirq `Circuit` jobs on Azure Quantum using `AzureQuantumService` from the QDK."
11 ]
12 },
13 {
14 "cell_type": "markdown",
15 "id": "0d696a7c",
16 "metadata": {},
17 "source": [
18 "The workflow demonstrated here:\n",
19 "\n",
20 "1. Build a Cirq `Circuit` with named measurement keys.\n",
21 "2. Reference an existing Azure Quantum workspace with `AzureQuantumService`.\n",
22 "3. Browse available targets via `service.targets()`.\n",
23 "4. Call `service.create_job(program=circuit, repetitions=..., target=...)` and fetch results (`job.results()`)."
24 ]
25 },
26 {
27 "cell_type": "markdown",
28 "id": "22d78680",
29 "metadata": {},
30 "source": [
31 "## Prerequisites\n",
32 "\n",
33 "This notebook assumes the `qdk` package with Azure Quantum and Cirq support is installed. You can install everything with:"
34 ]
35 },
36 {
37 "cell_type": "code",
38 "execution_count": null,
39 "id": "eb556730",
40 "metadata": {},
41 "outputs": [],
42 "source": [
43 "%pip install \"qdk[azure,cirq]\""
44 ]
45 },
46 {
47 "cell_type": "markdown",
48 "id": "20b9ed32",
49 "metadata": {},
50 "source": [
51 "This installs:\n",
52 "- The base `qdk` package (compiler, OpenQASM/QIR tooling)\n",
53 "- Azure Quantum client dependencies for submission\n",
54 "- Cirq for circuit construction\n",
55 "\n",
56 "After installing, restart the notebook kernel if it was already running. You can verify installation with:"
57 ]
58 },
59 {
60 "cell_type": "code",
61 "execution_count": null,
62 "id": "7531d5a6",
63 "metadata": {},
64 "outputs": [],
65 "source": [
66 "import cirq, qdk, qdk.azure # should import without errors"
67 ]
68 },
69 {
70 "cell_type": "markdown",
71 "id": "e2b111db",
72 "metadata": {},
73 "source": [
74 "## Build a simple Cirq circuit\n",
75 "\n",
76 "We start with a Bell-state circuit with two named measurement keys — one per qubit. Named keys are required so the result dictionary has clearly labeled registers."
77 ]
78 },
79 {
80 "cell_type": "code",
81 "execution_count": null,
82 "id": "db4e96e3",
83 "metadata": {},
84 "outputs": [],
85 "source": [
86 "import cirq\n",
87 "\n",
88 "q0, q1 = cirq.LineQubit.range(2)\n",
89 "circuit = cirq.Circuit(\n",
90 " cirq.H(q0),\n",
91 " cirq.CNOT(q0, q1),\n",
92 " cirq.measure(q0, key=\"q0\"),\n",
93 " cirq.measure(q1, key=\"q1\"),\n",
94 ")\n",
95 "print(circuit)"
96 ]
97 },
98 {
99 "cell_type": "markdown",
100 "id": "f582bc13",
101 "metadata": {},
102 "source": [
103 "## Configure Azure Quantum workspace connection\n",
104 "\n",
105 "To connect to an Azure workspace replace the following variables with your own values."
106 ]
107 },
108 {
109 "cell_type": "code",
110 "execution_count": null,
111 "id": "0344527e",
112 "metadata": {},
113 "outputs": [],
114 "source": [
115 "subscription_id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'\n",
116 "resource_group = 'myresourcegroup'\n",
117 "workspace_name = 'myworkspace'\n",
118 "location = 'westus'"
119 ]
120 },
121 {
122 "cell_type": "markdown",
123 "id": "184b6e57",
124 "metadata": {},
125 "source": [
126 "## Submit the circuit to Azure Quantum\n",
127 "\n",
128 "`AzureQuantumService` exposes Azure Quantum targets as Cirq-compatible target objects. When you call `service.targets()`, the SDK returns one of two kinds of target:\n",
129 "\n",
130 "- **Provider-specific targets** — Some hardware vendors (e.g. IonQ, Quantinuum) ship dedicated Cirq target classes with native integration for their APIs, handling gate translation and result parsing using hardware-specific logic.\n",
131 "- **Generic QIR targets** — For any other target that accepts QIR input, the SDK automatically wraps it as an `AzureGenericQirCirqTarget`. These compile the Cirq circuit to OpenQASM 3 and then to QIR internally, so you don't have to manage those steps manually.\n",
132 "\n",
133 "In practice `service.targets()` selects the right type for each target — you use the same `service.create_job()` call regardless of which target type is returned."
134 ]
135 },
136 {
137 "cell_type": "code",
138 "execution_count": null,
139 "id": "9f336702",
140 "metadata": {},
141 "outputs": [],
142 "source": [
143 "from qdk.azure import Workspace\n",
144 "from azure.quantum.cirq import AzureQuantumService\n",
145 "\n",
146 "workspace = Workspace(\n",
147 " subscription_id=subscription_id,\n",
148 " resource_group=resource_group,\n",
149 " name=workspace_name,\n",
150 " location=location,\n",
151 ")\n",
152 "\n",
153 "service = AzureQuantumService(workspace)\n",
154 "\n",
155 "# List available targets\n",
156 "for target in service.targets():\n",
157 " print(f\"{target.name:45s} {type(target).__name__}\")"
158 ]
159 },
160 {
161 "cell_type": "code",
162 "execution_count": null,
163 "id": "417d7818",
164 "metadata": {},
165 "outputs": [],
166 "source": [
167 "from collections import Counter\n",
168 "\n",
169 "# Replace with any target name from the list above\n",
170 "target_name = \"rigetti.sim.qvm\"\n",
171 "\n",
172 "job = service.create_job(\n",
173 " program=circuit,\n",
174 " repetitions=100,\n",
175 " name=\"cirq-bell-job\",\n",
176 " target=target_name,\n",
177 ")\n",
178 "print(f\"Job {job.job_id()} submitted — waiting for results...\")\n",
179 "\n",
180 "result = job.results()\n",
181 "\n",
182 "# Combine separate measurement keys into joint bitstrings\n",
183 "keys = sorted(result.measurements.keys())\n",
184 "joint = Counter(\n",
185 " \"\".join(str(int(result.measurements[k][i][0])) for k in keys)\n",
186 " for i in range(len(result.measurements[keys[0]]))\n",
187 ")\n",
188 "total = sum(joint.values())\n",
189 "print(f\"\\nResults ({total} shots) [keys: {', '.join(keys)}]:\")\n",
190 "for bitstring, count in sorted(joint.items()):\n",
191 " print(f\" {bitstring}: {count:4d} ({count/total:.1%})\")"
192 ]
193 },
194 {
195 "cell_type": "markdown",
196 "id": "1478e88a",
197 "metadata": {},
198 "source": [
199 "## Handling qubit loss on noisy hardware\n",
200 "\n",
201 "On some hardware backends — particularly neutral-atom and trapped-ion devices — a qubit may be lost before measurement (e.g. an atom is ejected from the trap). When this happens, the backend records `\"-\"` in the bitstring position for that qubit rather than `\"0\"` or `\"1\"`. Because loss shots contain non-binary characters, they cannot be included in standard measurement arrays, which assume a fixed binary alphabet. The SDK therefore separates them automatically.\n",
202 "\n",
203 "The `cirq.ResultDict` returned by `job.results()` exposes two ways to access shots:\n",
204 "\n",
205 "| Field | What it contains |\n",
206 "|---|---|\n",
207 "| **`result.measurements[key]`** | NumPy int8 array of accepted shots only (no `\"-\"`), shape `(accepted_shots, num_qubits)` |\n",
208 "| **`result.raw_measurements()[key]`** | String array of all shots (loss shots have `\"-\"`), same key structure as `measurements` |\n",
209 "| **`result.raw_shots`** | The original shot objects exactly as returned by the backend |\n",
210 "\n",
211 "Use `result.measurements` for any downstream analysis that expects clean binary arrays. Use `result.raw_measurements()` and `result.raw_shots` to inspect loss patterns — for example, to calculate the overall loss rate or identify which qubit positions are being lost most frequently.\n",
212 "\n",
213 "> **Tip**: A high loss rate may indicate hardware instability or a circuit that is too deep for the current calibration."
214 ]
215 }
216 ],
217 "metadata": {
218 "language_info": {
219 "name": "python"
220 }
221 },
222 "nbformat": 4,
223 "nbformat_minor": 5
224}
225