microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
samples/python_interop/openqasm.ipynb
485lines · modecode
| 1 | { |
| 2 | "cells": [ |
| 3 | { |
| 4 | "cell_type": "markdown", |
| 5 | "id": "ae56fce0", |
| 6 | "metadata": {}, |
| 7 | "source": [ |
| 8 | "# Q# Interop with OpenQASM" |
| 9 | ] |
| 10 | }, |
| 11 | { |
| 12 | "cell_type": "markdown", |
| 13 | "id": "2b838c23", |
| 14 | "metadata": {}, |
| 15 | "source": [ |
| 16 | "The modern QDK provides interoperability with OpenQASM 3 programs built upon the core Q# compiler infrastructure.\n", |
| 17 | "\n", |
| 18 | "This core enables integration and local resource estimation without relying on external tools. Users are able to estimate resources for their OpenQASM programs locally (see the [resource estimation with Qiskit sample notebook](../../estimation/estimation-openqasm.ipynb)), leveraging the Q# compiler's capabilities for analysis, transformation, code generation, and simulation. This also enables the generation of QIR from OpenQASM progams leveraging the [modern QDKs advanced code generation capabilities](https://devblogs.microsoft.com/qsharp/integrated-hybrid-support-in-the-azure-quantum-development-kit/).\n", |
| 19 | "\n", |
| 20 | "This includes support for classical instructions available in OpenQASM such as for loops, if statements, switch statements, while loops, binary expresssions, and more." |
| 21 | ] |
| 22 | }, |
| 23 | { |
| 24 | "cell_type": "markdown", |
| 25 | "id": "9fed5ba5", |
| 26 | "metadata": {}, |
| 27 | "source": [ |
| 28 | "Import the Q# module.\n", |
| 29 | "\n", |
| 30 | "This enables the `%%qsharp` magic and initializes a Q# interpreter singleton." |
| 31 | ] |
| 32 | }, |
| 33 | { |
| 34 | "cell_type": "code", |
| 35 | "execution_count": null, |
| 36 | "id": "75b8b81b", |
| 37 | "metadata": {}, |
| 38 | "outputs": [], |
| 39 | "source": [ |
| 40 | "import qsharp\n", |
| 41 | "qsharp.init(target_profile=qsharp.TargetProfile.Base)" |
| 42 | ] |
| 43 | }, |
| 44 | { |
| 45 | "cell_type": "markdown", |
| 46 | "id": "95d82f22", |
| 47 | "metadata": {}, |
| 48 | "source": [ |
| 49 | "### Run OpenQASM 3 Code in interactive session\n", |
| 50 | "Interactive sessions have different semantics from program execution. We no longer have inferred output and input. Instead we treat qasm lines as code fragments and interpret them one at a time (though they are all compiled together). Due to scoping rules in qasm3, all code used in the the program must be defined in the snippet and can't use compilation state from other cells or calls.\n", |
| 51 | "\n", |
| 52 | "We can add an optional name parameter to compile the program into a callable operation in the interactive session." |
| 53 | ] |
| 54 | }, |
| 55 | { |
| 56 | "cell_type": "code", |
| 57 | "execution_count": null, |
| 58 | "id": "012cc902", |
| 59 | "metadata": {}, |
| 60 | "outputs": [], |
| 61 | "source": [ |
| 62 | "%%qasm3 --name bell\n", |
| 63 | "include \"stdgates.inc\";\n", |
| 64 | "qubit[2] q;\n", |
| 65 | "h q[0];\n", |
| 66 | "cx q[0], q[1];\n", |
| 67 | "bit[2] c;\n", |
| 68 | "c[0] = measure q[0];\n", |
| 69 | "c[1] = measure q[1];" |
| 70 | ] |
| 71 | }, |
| 72 | { |
| 73 | "cell_type": "markdown", |
| 74 | "id": "b95a0c8b", |
| 75 | "metadata": {}, |
| 76 | "source": [ |
| 77 | "With the OpenQASM program loaded into a callable name `bell`, we can now import it via the QDK's Python bindings:" |
| 78 | ] |
| 79 | }, |
| 80 | { |
| 81 | "cell_type": "code", |
| 82 | "execution_count": null, |
| 83 | "id": "db043bda", |
| 84 | "metadata": {}, |
| 85 | "outputs": [], |
| 86 | "source": [ |
| 87 | "from qsharp.code import bell\n", |
| 88 | "bell()" |
| 89 | ] |
| 90 | }, |
| 91 | { |
| 92 | "cell_type": "markdown", |
| 93 | "id": "8db074dc", |
| 94 | "metadata": {}, |
| 95 | "source": [ |
| 96 | "Additionally, since it is defined in the session, we can run it directly from a Q# cell:" |
| 97 | ] |
| 98 | }, |
| 99 | { |
| 100 | "cell_type": "code", |
| 101 | "execution_count": null, |
| 102 | "id": "ad6d0331", |
| 103 | "metadata": { |
| 104 | "vscode": { |
| 105 | "languageId": "qsharp" |
| 106 | } |
| 107 | }, |
| 108 | "outputs": [], |
| 109 | "source": [ |
| 110 | "%%qsharp\n", |
| 111 | "bell()" |
| 112 | ] |
| 113 | }, |
| 114 | { |
| 115 | "cell_type": "markdown", |
| 116 | "id": "6578cadc", |
| 117 | "metadata": {}, |
| 118 | "source": [ |
| 119 | "This also unlocks all of the other `qsharp` package functionality. Like noisy simulation:" |
| 120 | ] |
| 121 | }, |
| 122 | { |
| 123 | "cell_type": "code", |
| 124 | "execution_count": null, |
| 125 | "id": "5bbd6d92", |
| 126 | "metadata": {}, |
| 127 | "outputs": [], |
| 128 | "source": [ |
| 129 | "from qsharp_widgets import Histogram\n", |
| 130 | "\n", |
| 131 | "Histogram(qsharp.run(\"bell()\", shots=1000, noise=qsharp.DepolarizingNoise(0.01)))" |
| 132 | ] |
| 133 | }, |
| 134 | { |
| 135 | "cell_type": "markdown", |
| 136 | "id": "85d48772", |
| 137 | "metadata": {}, |
| 138 | "source": [ |
| 139 | "Circuit rendering:" |
| 140 | ] |
| 141 | }, |
| 142 | { |
| 143 | "cell_type": "code", |
| 144 | "execution_count": null, |
| 145 | "id": "a0a50634", |
| 146 | "metadata": {}, |
| 147 | "outputs": [], |
| 148 | "source": [ |
| 149 | "qsharp.circuit(qsharp.code.bell)" |
| 150 | ] |
| 151 | }, |
| 152 | { |
| 153 | "cell_type": "markdown", |
| 154 | "id": "5865ea0c", |
| 155 | "metadata": {}, |
| 156 | "source": [ |
| 157 | "Circuit widget rendering:" |
| 158 | ] |
| 159 | }, |
| 160 | { |
| 161 | "cell_type": "code", |
| 162 | "execution_count": null, |
| 163 | "id": "c3fc87ae", |
| 164 | "metadata": {}, |
| 165 | "outputs": [], |
| 166 | "source": [ |
| 167 | "from qsharp_widgets import Circuit\n", |
| 168 | "Circuit(qsharp.circuit(qsharp.code.bell))" |
| 169 | ] |
| 170 | }, |
| 171 | { |
| 172 | "cell_type": "markdown", |
| 173 | "id": "0e1a29c8", |
| 174 | "metadata": {}, |
| 175 | "source": [ |
| 176 | "Code generation:" |
| 177 | ] |
| 178 | }, |
| 179 | { |
| 180 | "cell_type": "code", |
| 181 | "execution_count": null, |
| 182 | "id": "ec6db1e5", |
| 183 | "metadata": {}, |
| 184 | "outputs": [], |
| 185 | "source": [ |
| 186 | "print(qsharp.compile(bell))" |
| 187 | ] |
| 188 | }, |
| 189 | { |
| 190 | "cell_type": "markdown", |
| 191 | "id": "759f6a5e", |
| 192 | "metadata": {}, |
| 193 | "source": [ |
| 194 | "We can also define input for the compiled OpenQASM code so that we can parameterize input:" |
| 195 | ] |
| 196 | }, |
| 197 | { |
| 198 | "cell_type": "code", |
| 199 | "execution_count": null, |
| 200 | "id": "bd179f1a", |
| 201 | "metadata": {}, |
| 202 | "outputs": [], |
| 203 | "source": [ |
| 204 | "%%qasm3 --name needs_args\n", |
| 205 | "include \"stdgates.inc\";\n", |
| 206 | "input float theta;\n", |
| 207 | "qubit[2] q;\n", |
| 208 | "rx(theta) q[0];\n", |
| 209 | "rx(-theta) q[1];\n", |
| 210 | "bit[2] c;\n", |
| 211 | "c[0] = measure q[0];\n", |
| 212 | "c[1] = measure q[1];" |
| 213 | ] |
| 214 | }, |
| 215 | { |
| 216 | "cell_type": "code", |
| 217 | "execution_count": null, |
| 218 | "id": "5b7a97d2", |
| 219 | "metadata": {}, |
| 220 | "outputs": [], |
| 221 | "source": [ |
| 222 | "from qsharp.code import needs_args\n", |
| 223 | "print(qsharp.compile(needs_args, 1.57))" |
| 224 | ] |
| 225 | }, |
| 226 | { |
| 227 | "cell_type": "markdown", |
| 228 | "id": "3a821443", |
| 229 | "metadata": {}, |
| 230 | "source": [ |
| 231 | "Any library that exports to OpenQASM 3 can now integrate into the QDK's ecosystem.\n", |
| 232 | "\n", |
| 233 | "Let's take a look at cirq for example" |
| 234 | ] |
| 235 | }, |
| 236 | { |
| 237 | "cell_type": "code", |
| 238 | "execution_count": null, |
| 239 | "id": "b7ee6d8b", |
| 240 | "metadata": {}, |
| 241 | "outputs": [], |
| 242 | "source": [ |
| 243 | "try:\n", |
| 244 | " import cirq\n", |
| 245 | "except ImportError:\n", |
| 246 | " print(\"installing cirq...\")\n", |
| 247 | " !pip install cirq\n", |
| 248 | " import cirq\n", |
| 249 | " print(\"installed cirq.\")" |
| 250 | ] |
| 251 | }, |
| 252 | { |
| 253 | "cell_type": "markdown", |
| 254 | "id": "47b1f488", |
| 255 | "metadata": {}, |
| 256 | "source": [ |
| 257 | "With cirq installed, we'll build up some utilities to create a Bernstein-Vazirani circuit" |
| 258 | ] |
| 259 | }, |
| 260 | { |
| 261 | "cell_type": "code", |
| 262 | "execution_count": null, |
| 263 | "id": "77c85028", |
| 264 | "metadata": {}, |
| 265 | "outputs": [], |
| 266 | "source": [ |
| 267 | "import random\n", |
| 268 | "import cirq\n", |
| 269 | "\n", |
| 270 | "def make_oracle(input_qubits, output_qubit, secret_factor_bits, secret_bias_bit):\n", |
| 271 | " \"\"\"Gates implementing the function f(a) = a·factors + bias (mod 2).\"\"\"\n", |
| 272 | "\n", |
| 273 | " if secret_bias_bit:\n", |
| 274 | " yield cirq.X(output_qubit)\n", |
| 275 | "\n", |
| 276 | " for qubit, bit in zip(input_qubits, secret_factor_bits):\n", |
| 277 | " if bit: # pragma: no cover\n", |
| 278 | " yield cirq.CNOT(qubit, output_qubit)\n", |
| 279 | "\n", |
| 280 | "\n", |
| 281 | "def make_bernstein_vazirani_circuit_with_cirq(input_qubits, output_qubit, oracle):\n", |
| 282 | " \"\"\"Solves for factors in f(a) = a·factors + bias (mod 2) with one query.\"\"\"\n", |
| 283 | "\n", |
| 284 | " c = cirq.Circuit()\n", |
| 285 | "\n", |
| 286 | " # Initialize qubits.\n", |
| 287 | " c.append([cirq.X(output_qubit), cirq.H(output_qubit), cirq.H.on_each(*input_qubits)])\n", |
| 288 | "\n", |
| 289 | " # Query oracle.\n", |
| 290 | " c.append(oracle)\n", |
| 291 | "\n", |
| 292 | " # Measure in X basis.\n", |
| 293 | " c.append([cirq.H.on_each(*input_qubits), cirq.measure(*input_qubits, key='result')])\n", |
| 294 | "\n", |
| 295 | " return c\n", |
| 296 | "\n", |
| 297 | "\n", |
| 298 | "def bitstring(bits):\n", |
| 299 | " return ''.join(str(int(b)) for b in bits)" |
| 300 | ] |
| 301 | }, |
| 302 | { |
| 303 | "cell_type": "markdown", |
| 304 | "id": "514773b6", |
| 305 | "metadata": {}, |
| 306 | "source": [ |
| 307 | "And we'll call the code to build the circuit" |
| 308 | ] |
| 309 | }, |
| 310 | { |
| 311 | "cell_type": "code", |
| 312 | "execution_count": null, |
| 313 | "id": "c8e1746a", |
| 314 | "metadata": {}, |
| 315 | "outputs": [], |
| 316 | "source": [ |
| 317 | "import qsharp\n", |
| 318 | "from qsharp.interop.qasm3 import eval, estimate, import_callable\n", |
| 319 | "from qsharp import init\n", |
| 320 | "init(target_profile=qsharp.TargetProfile.Base)\n", |
| 321 | "\n", |
| 322 | "qubit_count=8\n", |
| 323 | "\n", |
| 324 | "# Choose qubits to use.\n", |
| 325 | "input_qubits = [cirq.GridQubit(i, 0) for i in range(qubit_count)]\n", |
| 326 | "output_qubit = cirq.GridQubit(qubit_count, 0)\n", |
| 327 | "\n", |
| 328 | "# Pick coefficients for the oracle and create a circuit to query it.\n", |
| 329 | "secret_bias_bit = random.randint(0, 1)\n", |
| 330 | "secret_factor_bits = [random.randint(0, 1) for _ in range(qubit_count)]\n", |
| 331 | "oracle = make_oracle(input_qubits, output_qubit, secret_factor_bits, secret_bias_bit)\n", |
| 332 | "print(\n", |
| 333 | " 'Secret function:\\nf(a) = '\n", |
| 334 | " f\"a·<{', '.join(str(e) for e in secret_factor_bits)}> + \"\n", |
| 335 | " f\"{secret_bias_bit} (mod 2)\"\n", |
| 336 | ")\n", |
| 337 | "\n", |
| 338 | "circuit = make_bernstein_vazirani_circuit_with_cirq(input_qubits, output_qubit, oracle)" |
| 339 | ] |
| 340 | }, |
| 341 | { |
| 342 | "cell_type": "markdown", |
| 343 | "id": "683d3d8d", |
| 344 | "metadata": {}, |
| 345 | "source": [ |
| 346 | "We can now see the OpenQASM 3 equivalent program" |
| 347 | ] |
| 348 | }, |
| 349 | { |
| 350 | "cell_type": "code", |
| 351 | "execution_count": null, |
| 352 | "id": "ea2acea4", |
| 353 | "metadata": {}, |
| 354 | "outputs": [], |
| 355 | "source": [ |
| 356 | "qasm_str = cirq.qasm(circuit, args=cirq.QasmArgs(version=\"3.0\"))\n", |
| 357 | "print(qasm_str)" |
| 358 | ] |
| 359 | }, |
| 360 | { |
| 361 | "cell_type": "markdown", |
| 362 | "id": "deeaad51", |
| 363 | "metadata": {}, |
| 364 | "source": [ |
| 365 | "And leverage the QDK resource estimation capabilities for the cirq circuit" |
| 366 | ] |
| 367 | }, |
| 368 | { |
| 369 | "cell_type": "code", |
| 370 | "execution_count": null, |
| 371 | "id": "1a0c5d95", |
| 372 | "metadata": {}, |
| 373 | "outputs": [], |
| 374 | "source": [ |
| 375 | "# use QDK resource estimation on cirq circuit via QASM3\n", |
| 376 | "estimate(cirq.qasm(circuit, args=cirq.QasmArgs(version=\"3.0\")))" |
| 377 | ] |
| 378 | }, |
| 379 | { |
| 380 | "cell_type": "markdown", |
| 381 | "id": "1fd7f006", |
| 382 | "metadata": {}, |
| 383 | "source": [ |
| 384 | "If a library allows for classical controls or branching, any ouput OpenQASM they generate is consumable:" |
| 385 | ] |
| 386 | }, |
| 387 | { |
| 388 | "cell_type": "code", |
| 389 | "execution_count": null, |
| 390 | "id": "fd1a046a", |
| 391 | "metadata": {}, |
| 392 | "outputs": [], |
| 393 | "source": [ |
| 394 | "q0, q1 = cirq.LineQubit.range(2)\n", |
| 395 | "circuit = cirq.Circuit(\n", |
| 396 | " cirq.H(q0),\n", |
| 397 | " cirq.measure(q0, key='a'),\n", |
| 398 | " cirq.H(q1).with_classical_controls('a'),\n", |
| 399 | " cirq.measure(q1, key='b'),\n", |
| 400 | ")\n", |
| 401 | "print(circuit)\n", |
| 402 | "print(cirq.qasm(circuit, args=cirq.QasmArgs(version=\"3.0\")))" |
| 403 | ] |
| 404 | }, |
| 405 | { |
| 406 | "cell_type": "code", |
| 407 | "execution_count": null, |
| 408 | "id": "e7a21606", |
| 409 | "metadata": {}, |
| 410 | "outputs": [], |
| 411 | "source": [ |
| 412 | "alice = cirq.NamedQubit('alice')\n", |
| 413 | "bob = cirq.NamedQubit('bob')\n", |
| 414 | "message = cirq.NamedQubit('_message')\n", |
| 415 | "\n", |
| 416 | "message_circuit = cirq.Circuit(\n", |
| 417 | " # Create the message.\n", |
| 418 | " cirq.X(message) ** 0.371,\n", |
| 419 | " cirq.Y(message) ** 0.882,\n", |
| 420 | ")\n", |
| 421 | "\n", |
| 422 | "circuit = cirq.Circuit(\n", |
| 423 | " # Create Bell state to be shared between Alice and Bob.\n", |
| 424 | " cirq.H(alice),\n", |
| 425 | " cirq.CNOT(alice, bob),\n", |
| 426 | " # Prepare message circuit\n", |
| 427 | " message_circuit,\n", |
| 428 | " # Bell measurement of the message and Alice's entangled qubit.\n", |
| 429 | " cirq.CNOT(message, alice),\n", |
| 430 | " cirq.H(message),\n", |
| 431 | " cirq.measure(message, key='M'),\n", |
| 432 | " cirq.measure(alice, key='A'),\n", |
| 433 | " # Uses the two classical bits from the Bell measurement to recover the\n", |
| 434 | " # original quantum message on Bob's entangled qubit.\n", |
| 435 | " cirq.X(bob).with_classical_controls('A'),\n", |
| 436 | " cirq.Z(bob).with_classical_controls('M'),\n", |
| 437 | ")\n", |
| 438 | "print(circuit)\n", |
| 439 | "print(cirq.qasm(circuit, args=cirq.QasmArgs(version=\"3.0\")))" |
| 440 | ] |
| 441 | }, |
| 442 | { |
| 443 | "cell_type": "code", |
| 444 | "execution_count": null, |
| 445 | "id": "c29d9f86", |
| 446 | "metadata": {}, |
| 447 | "outputs": [], |
| 448 | "source": [ |
| 449 | "import cirq\n", |
| 450 | "from qsharp.interop.qasm3 import estimate\n", |
| 451 | "\n", |
| 452 | "q0 = cirq.LineQubit(0)\n", |
| 453 | "subcircuit = cirq.FrozenCircuit(cirq.measure(q0, key='a'), cirq.X(q0).with_classical_controls('a'))\n", |
| 454 | "circuit = cirq.Circuit(\n", |
| 455 | " cirq.measure(q0, key='a'),\n", |
| 456 | " cirq.CircuitOperation(subcircuit, repetitions=2),\n", |
| 457 | " cirq.X(q0).with_classical_controls('a'),\n", |
| 458 | ")\n", |
| 459 | "print(cirq.qasm(circuit, args=cirq.QasmArgs(version=\"3.0\")))\n", |
| 460 | "estimate(cirq.qasm(circuit, args=cirq.QasmArgs(version=\"3.0\")))" |
| 461 | ] |
| 462 | } |
| 463 | ], |
| 464 | "metadata": { |
| 465 | "kernelspec": { |
| 466 | "display_name": ".venv", |
| 467 | "language": "python", |
| 468 | "name": "python3" |
| 469 | }, |
| 470 | "language_info": { |
| 471 | "codemirror_mode": { |
| 472 | "name": "ipython", |
| 473 | "version": 3 |
| 474 | }, |
| 475 | "file_extension": ".py", |
| 476 | "mimetype": "text/x-python", |
| 477 | "name": "python", |
| 478 | "nbconvert_exporter": "python", |
| 479 | "pygments_lexer": "ipython3", |
| 480 | "version": "3.12.9" |
| 481 | } |
| 482 | }, |
| 483 | "nbformat": 4, |
| 484 | "nbformat_minor": 5 |
| 485 | } |
| 486 | |