microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
amcasey/WrapInArray

Branches

Tags

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

Clone

HTTPS

Download ZIP

samples/notebooks/correlated_noise.ipynb

410lines · modecode

1{
2 "cells": [
3 {
4 "cell_type": "markdown",
5 "id": "b7675b9f",
6 "metadata": {},
7 "source": [
8 "# Correlated Noise Simulation\n",
9 "\n",
10 "This notebook demonstrates how to simulate **correlated (multi-qubit) noise** using the QDK simulators.\n",
11 "While the [noise.ipynb](noise.ipynb) notebook covers independent Pauli noise applied uniformly to all gates,\n",
12 "this notebook shows how to:\n",
13 "\n",
14 "1. Configure per-gate noise using `NoiseConfig`.\n",
15 "2. Define custom noise intrinsics to model multi-qubit noise for custom gates.\n",
16 "\n",
17 "First, make sure prerequisites are available. The package and extra `qdk[jupyter]` must be already installed."
18 ]
19 },
20 {
21 "cell_type": "code",
22 "execution_count": null,
23 "id": "3f51ef59",
24 "metadata": {},
25 "outputs": [],
26 "source": [
27 "import qdk\n",
28 "from qdk import qsharp, openqasm\n",
29 "from qdk.simulation import NoiseConfig, run_qir\n",
30 "from qdk.widgets import Histogram"
31 ]
32 },
33 {
34 "cell_type": "markdown",
35 "id": "3944616a",
36 "metadata": {},
37 "source": [
38 "## 1. Per-Gate Noise with `NoiseConfig`\n",
39 "\n",
40 "The convenience functions `DepolarizingNoise(p)` and `BitFlipNoise(p)` apply the same noise\n",
41 "to every gate. For more realistic modeling, you often need different error rates for different\n",
42 "gates, e.g., two-qubit gates are typically noisier than single-qubit gates, and they might apply\n",
43 "different noise to each qubit.\n",
44 "\n",
45 "`NoiseConfig` gives you a noise table for each gate (H, X, CNOT, etc.), and each table\n",
46 "supports arbitrary Pauli error strings."
47 ]
48 },
49 {
50 "cell_type": "markdown",
51 "id": "fbbeb079",
52 "metadata": {},
53 "source": [
54 "### Bell pair with per-gate noise\n",
55 "\n",
56 "Define a Bell pair circuit in Q# and run it with different noise rates on single-qubit vs. two-qubit gates."
57 ]
58 },
59 {
60 "cell_type": "code",
61 "execution_count": null,
62 "id": "65ae7014",
63 "metadata": {
64 "vscode": {
65 "languageId": "qsharp"
66 }
67 },
68 "outputs": [],
69 "source": [
70 "%%qsharp\n",
71 "\n",
72 "operation BellPair() : Result[] {\n",
73 " use qs = Qubit[2];\n",
74 " H(qs[0]);\n",
75 " CNOT(qs[0], qs[1]);\n",
76 " MResetEachZ(qs)\n",
77 "}"
78 ]
79 },
80 {
81 "cell_type": "markdown",
82 "id": "e9d78333",
83 "metadata": {},
84 "source": [
85 "First, run without noise to establish a baseline. We expect only `[Zero, Zero]` and `[One, One]`."
86 ]
87 },
88 {
89 "cell_type": "code",
90 "execution_count": null,
91 "id": "aa9be062",
92 "metadata": {},
93 "outputs": [],
94 "source": [
95 "result = qsharp.run(\"BellPair()\", 1000, seed=42)\n",
96 "Histogram(result)"
97 ]
98 },
99 {
100 "cell_type": "markdown",
101 "id": "91fd10e8",
102 "metadata": {},
103 "source": [
104 "Now configure `NoiseConfig` with different noise rates per gate:\n",
105 "- H gate: 0.5% depolarizing (single-qubit gates are relatively clean)\n",
106 "- CNOT (cx): 5% depolarizing (two-qubit gates are typically 10× noisier)\n",
107 "- Measurement (mresetz): 1% bit-flip (measurement readout error)"
108 ]
109 },
110 {
111 "cell_type": "code",
112 "execution_count": null,
113 "id": "e62c90e2",
114 "metadata": {},
115 "outputs": [],
116 "source": [
117 "noise = NoiseConfig()\n",
118 "noise.h.set_depolarizing(0.005)\n",
119 "noise.cx.set_depolarizing(0.05)\n",
120 "noise.mresetz.set_bitflip(0.01)\n",
121 "\n",
122 "result = qsharp.run(\"BellPair()\", 1000, noise=noise, seed=42)\n",
123 "Histogram(result)"
124 ]
125 },
126 {
127 "cell_type": "markdown",
128 "id": "f231a569",
129 "metadata": {},
130 "source": [
131 "The histogram now shows some `[Zero, One]` and `[One, Zero]` outcomes from errors.\n",
132 "Since the CNOT has 10× more noise than the H gate, most errors come from the two-qubit gate."
133 ]
134 },
135 {
136 "cell_type": "markdown",
137 "id": "443c3f27",
138 "metadata": {},
139 "source": [
140 "### Arbitrary Pauli errors on a gate\n",
141 "\n",
142 "Each `NoiseTable` supports arbitrary Pauli strings. For example, on a CNOT gate we can\n",
143 "set specific correlated errors using multi-character Pauli strings. Each character in the\n",
144 "string corresponds to a qubit argument of the gate:\n",
145 "\n",
146 "| Pauli string | Meaning |\n",
147 "|---|---|\n",
148 "| `xi` | X error on control, identity on target |\n",
149 "| `ix` | Identity on control, X error on target |\n",
150 "| `xx` | Correlated X error on both qubits simultaneously |\n",
151 "| `zz` | Correlated Z (dephasing) on both qubits |"
152 ]
153 },
154 {
155 "cell_type": "code",
156 "execution_count": null,
157 "id": "6bbb9c77",
158 "metadata": {},
159 "outputs": [],
160 "source": [
161 "# Model a CNOT gate with 5% correlated IX and 2% correlated ZI on both qubits.\n",
162 "noise = NoiseConfig()\n",
163 "noise.cx.ix = 0.05 # 5% probability of correlated IX error\n",
164 "noise.cx.zi = 0.02 # 5% probability of correlated ZI error\n",
165 "\n",
166 "result = qsharp.run(\"BellPair()\", 1000, noise=noise, seed=42)\n",
167 "Histogram(result)"
168 ]
169 },
170 {
171 "cell_type": "markdown",
172 "id": "aa361125",
173 "metadata": {},
174 "source": [
175 "You can also set multiple Pauli error channels at once using `set_pauli_noise`:"
176 ]
177 },
178 {
179 "cell_type": "code",
180 "execution_count": null,
181 "id": "3bbbdbf0",
182 "metadata": {},
183 "outputs": [],
184 "source": [
185 "noise = NoiseConfig()\n",
186 "noise.cx.set_pauli_noise([\n",
187 " (\"XI\", 0.01), # 1% X error on control only\n",
188 " (\"IX\", 0.01), # 1% X error on target only\n",
189 " (\"XX\", 0.005), # 0.5% correlated XX\n",
190 " (\"ZZ\", 0.02), # 2% correlated ZZ crosstalk\n",
191 "])\n",
192 "\n",
193 "result = qsharp.run(\"BellPair()\", 1000, noise=noise, seed=42)\n",
194 "Histogram(result)"
195 ]
196 },
197 {
198 "cell_type": "markdown",
199 "id": "130889ea",
200 "metadata": {},
201 "source": [
202 "## 2. Custom Noise Intrinsics\n",
203 "\n",
204 "For more complex noise models, you can define custom noise intrinsics. These\n",
205 "are no-op gates in the ideal circuit that act as insertion points for correlated noise during\n",
206 "simulation. This is useful for modeling correlated noise on custom gates (those not included in `NoiseConfig`).\n",
207 "\n",
208 "A noise intrinsic is declared with `@NoiseIntrinsic()` in Q# (or `@qdk.qir.noise_intrinsic`\n",
209 "in OpenQASM). The noise model is then configured in Python via `NoiseConfig.intrinsic()`."
210 ]
211 },
212 {
213 "cell_type": "markdown",
214 "id": "adc6a923",
215 "metadata": {},
216 "source": [
217 "### Q# example: modeling crosstalk between qubits\n",
218 "\n",
219 "Imagine a 3-qubit register where applying a gate on qubit 0 causes crosstalk\n",
220 "(correlated ZZ dephasing) on qubits 1 and 2. We model this by inserting a noise\n",
221 "intrinsic after the gate."
222 ]
223 },
224 {
225 "cell_type": "code",
226 "execution_count": null,
227 "id": "e0df5c17",
228 "metadata": {},
229 "outputs": [],
230 "source": [
231 "qdk.init(target_profile=qdk.TargetProfile.Adaptive_RIF)"
232 ]
233 },
234 {
235 "cell_type": "code",
236 "execution_count": null,
237 "id": "51340462",
238 "metadata": {
239 "vscode": {
240 "languageId": "qsharp"
241 }
242 },
243 "outputs": [],
244 "source": [
245 "%%qsharp\n",
246 "\n",
247 "// A noise intrinsic representing crosstalk on 3 qubits.\n",
248 "// In the ideal circuit this is a no-op; the simulator injects\n",
249 "// Pauli errors according to the NoiseConfig.\n",
250 "@NoiseIntrinsic()\n",
251 "operation Crosstalk3Q(q0: Qubit, q1: Qubit, q2: Qubit) : Unit {\n",
252 " body intrinsic;\n",
253 "}\n",
254 "\n",
255 "// Prepare a GHZ state on 3 qubits, with crosstalk after each CNOT.\n",
256 "operation GHZ() : Result[] {\n",
257 " use qs = Qubit[3];\n",
258 " H(qs[0]);\n",
259 " CNOT(qs[0], qs[1]);\n",
260 " Crosstalk3Q(qs[0], qs[1], qs[2]); // crosstalk hits all 3 qubits\n",
261 " CNOT(qs[1], qs[2]);\n",
262 " Crosstalk3Q(qs[0], qs[1], qs[2]); // crosstalk again\n",
263 " MResetEachZ(qs)\n",
264 "}"
265 ]
266 },
267 {
268 "cell_type": "markdown",
269 "id": "ce61b334",
270 "metadata": {},
271 "source": [
272 "Compile to QIR so we can run with a custom `NoiseConfig`:"
273 ]
274 },
275 {
276 "cell_type": "code",
277 "execution_count": null,
278 "id": "1b211a19",
279 "metadata": {},
280 "outputs": [],
281 "source": [
282 "qir = qsharp.compile(\"GHZ()\")"
283 ]
284 },
285 {
286 "cell_type": "markdown",
287 "id": "4e431ff4",
288 "metadata": {},
289 "source": [
290 "First, run without noise, we should see a clean GHZ state (`000` and `111` only):"
291 ]
292 },
293 {
294 "cell_type": "code",
295 "execution_count": null,
296 "id": "6b7ed62a",
297 "metadata": {},
298 "outputs": [],
299 "source": [
300 "result = run_qir(qir, shots=1000, seed=42)\n",
301 "Histogram(result)"
302 ]
303 },
304 {
305 "cell_type": "markdown",
306 "id": "3155ed57",
307 "metadata": {},
308 "source": [
309 "Now configure the crosstalk noise intrinsic. We'll model two types of correlated errors:\n",
310 "- `ixx`: correlated XX on qubits 1 and 2 (10% probability)\n",
311 "- `xxi`: correlated XX on qubits 0 and 1 (5% probability)"
312 ]
313 },
314 {
315 "cell_type": "code",
316 "execution_count": null,
317 "id": "958335ff",
318 "metadata": {},
319 "outputs": [],
320 "source": [
321 "noise = NoiseConfig()\n",
322 "table = noise.intrinsic(\"Crosstalk3Q\", num_qubits=3)\n",
323 "table.ixx = 0.10 # 10% XX on qubits 1-2\n",
324 "table.xxi = 0.05 # 5% XX on qubits 0-1\n",
325 "\n",
326 "result = run_qir(qir, shots=1000, noise=noise, seed=42)\n",
327 "Histogram(result)"
328 ]
329 },
330 {
331 "cell_type": "markdown",
332 "id": "f18d9f40",
333 "metadata": {},
334 "source": [
335 "The crosstalk breaks the perfect GHZ correlations, we now see outcomes like `001`, `010`,\n",
336 "etc. that would be impossible in the ideal circuit."
337 ]
338 },
339 {
340 "cell_type": "markdown",
341 "id": "d9cd8528",
342 "metadata": {},
343 "source": [
344 "### OpenQASM example\n",
345 "\n",
346 "The same noise intrinsic pattern works in OpenQASM using the `@qdk.qir.noise_intrinsic`\n",
347 "annotation on a custom gate definition."
348 ]
349 },
350 {
351 "cell_type": "code",
352 "execution_count": null,
353 "id": "8ec4beaa",
354 "metadata": {},
355 "outputs": [],
356 "source": [
357 "qasm_source = \"\"\"\n",
358 "OPENQASM 3.0;\n",
359 "include \"stdgates.inc\";\n",
360 "\n",
361 "// A noise intrinsic representing crosstalk on 3 qubits.\n",
362 "// In the ideal circuit this is a no-op; the simulator injects\n",
363 "// Pauli errors according to the NoiseConfig.\n",
364 "@qdk.qir.noise_intrinsic\n",
365 "gate crosstalk_3q q0, q1, q2 {}\n",
366 "\n",
367 "qubit[3] qs;\n",
368 "\n",
369 "// Prepare a GHZ state on 3 qubits, with crosstalk after each CNOT.\n",
370 "h qs[0];\n",
371 "cx qs[0], qs[1];\n",
372 "crosstalk_3q qs[0], qs[1], qs[2]; // crosstalk hits all 3 qubits\n",
373 "cx qs[1], qs[2];\n",
374 "crosstalk_3q qs[0], qs[1], qs[2]; // crosstalk again\n",
375 "\n",
376 "bit[3] res = measure qs;\n",
377 "\"\"\"\n",
378 "\n",
379 "qir_qasm = openqasm.compile(\n",
380 " qasm_source,\n",
381 " output_semantics=openqasm.OutputSemantics.OpenQasm,\n",
382 " target_profile=qdk.TargetProfile.Base,\n",
383 ")"
384 ]
385 },
386 {
387 "cell_type": "code",
388 "execution_count": null,
389 "id": "a3d230ea",
390 "metadata": {},
391 "outputs": [],
392 "source": [
393 "noise = NoiseConfig()\n",
394 "table = noise.intrinsic(\"crosstalk_3q\", num_qubits=3)\n",
395 "table.ixx = 0.10 # 10% XX on qubits 1-2\n",
396 "table.xxi = 0.05 # 5% XX on qubits 0-1\n",
397 "\n",
398 "result = run_qir(qir_qasm, shots=1000, noise=noise, seed=42)\n",
399 "Histogram(result)"
400 ]
401 }
402 ],
403 "metadata": {
404 "language_info": {
405 "name": "python"
406 }
407 },
408 "nbformat": 4,
409 "nbformat_minor": 5
410}
411