{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "ae56fce0",
      "metadata": {},
      "source": [
        "# Q# Interop with OpenQASM"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2b838c23",
      "metadata": {},
      "source": [
        "The modern QDK provides interoperability with OpenQASM 3 programs built upon the core Q# compiler infrastructure.\n",
        "\n",
        "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",
        "\n",
        "This includes support for classical instructions available in OpenQASM such as for loops, if statements, switch statements, while loops, binary expresssions, and more."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4f761649",
      "metadata": {},
      "source": [
        "### Run OpenQASM 3 Code in interactive session\n",
        "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 OpenQASM, 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",
        "\n",
        "\n",
        "\n",
        "Import the Q# module.\n",
        "\n",
        "This enables the `%%openqasm` (and `%%qsharp`) magic and initializes a interpreter singleton."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "e91dd2d7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "application/javascript": "// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\n// This file provides CodeMirror syntax highlighting for Q# magic cells\n// in classic Jupyter Notebooks. It does nothing in other (Jupyter Notebook 7,\n// VS Code, Azure Notebooks, etc.) environments.\n\n// Detect the prerequisites and do nothing if they don't exist.\nif (window.require && window.CodeMirror && window.Jupyter) {\n  // The simple mode plugin for CodeMirror is not loaded by default, so require it.\n  window.require([\"codemirror/addon/mode/simple\"], function defineMode() {\n    let rules = [\n      {\n        token: \"comment\",\n        regex: /(\\/\\/).*/,\n        beginWord: false,\n      },\n      {\n        token: \"string\",\n        regex: String.raw`^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)`,\n        beginWord: false,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(namespace|open|as|operation|function|body|adjoint|newtype|controlled|internal)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(if|elif|else|repeat|until|fixup|for|in|return|fail|within|apply)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(Adjoint|Controlled|Adj|Ctl|is|self|auto|distribute|invert|intrinsic)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"keyword\",\n        regex: String.raw`(let|set|use|borrow|mutable)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"operatorKeyword\",\n        regex: String.raw`(not|and|or)\\b|(w/)`,\n        beginWord: true,\n      },\n      {\n        token: \"operatorKeyword\",\n        regex: String.raw`(=)|(!)|(<)|(>)|(\\+)|(-)|(\\*)|(/)|(\\^)|(%)|(\\|)|(&&&)|(~~~)|(\\.\\.\\.)|(\\.\\.)|(\\?)`,\n        beginWord: false,\n      },\n      {\n        token: \"meta\",\n        regex: String.raw`(Int|BigInt|Double|Bool|Qubit|Pauli|Result|Range|String|Unit)\\b`,\n        beginWord: true,\n      },\n      {\n        token: \"atom\",\n        regex: String.raw`(true|false|Pauli(I|X|Y|Z)|One|Zero)\\b`,\n        beginWord: true,\n      },\n    ];\n    let simpleRules = [];\n    for (let rule of rules) {\n      simpleRules.push({\n        token: rule.token,\n        regex: new RegExp(rule.regex, \"g\"),\n        sol: rule.beginWord,\n      });\n      if (rule.beginWord) {\n        // Need an additional rule due to the fact that CodeMirror simple mode doesn't work with ^ token\n        simpleRules.push({\n          token: rule.token,\n          regex: new RegExp(String.raw`\\W` + rule.regex, \"g\"),\n          sol: false,\n        });\n      }\n    }\n\n    // Register the mode defined above with CodeMirror\n    window.CodeMirror.defineSimpleMode(\"qsharp\", { start: simpleRules });\n    window.CodeMirror.defineMIME(\"text/x-qsharp\", \"qsharp\");\n\n    // Tell Jupyter to associate %%qsharp magic cells with the qsharp mode\n    window.Jupyter.CodeCell.options_default.highlight_modes[\"qsharp\"] = {\n      reg: [/^%%qsharp/],\n    };\n\n    // Force re-highlighting of all cells the first time this code runs\n    for (const cell of window.Jupyter.notebook.get_cells()) {\n      cell.auto_highlight();\n    }\n  });\n}\n",
            "text/plain": []
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "application/x.qsharp-config": "{\"targetProfile\":\"base\",\"languageFeatures\":null,\"manifest\":null}",
            "text/plain": [
              "Q# initialized with configuration: {'targetProfile': 'base', 'languageFeatures': None, 'manifest': None}"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import qsharp\n",
        "qsharp.init(target_profile=qsharp.TargetProfile.Base)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "922f8420",
      "metadata": {},
      "source": [
        "With the runtime initialized we can now run OpenQASM programs in their own cells with the `%%qasm` magic. Running this cell will simulate the program using the default simulator and its output will be displayed."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "7c69aeac",
      "metadata": {
        "vscode": {
          "languageId": "openqasm"
        }
      },
      "outputs": [],
      "source": [
        "%%openqasm --shots 1024\n",
        "include \"stdgates.inc\";\n",
        "qubit[2] q;\n",
        "h q[0];\n",
        "cx q[0], q[1];\n",
        "bit[2] c;\n",
        "c[0] = measure q[0];\n",
        "c[1] = measure q[1];"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "62b2970d",
      "metadata": {},
      "source": [
        "We can leverage the Jupyter `_` variable to access the run output from the previous cell  if we want to work with the output."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "c80ddb17",
      "metadata": {},
      "outputs": [],
      "source": [
        "last_output = _ # type: ignore\n",
        "print(last_output)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ffd986f4",
      "metadata": {},
      "source": [
        "We can add an optional `name` parameter to compile the program into a callable operation in the interactive session."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2c1c9a73",
      "metadata": {
        "vscode": {
          "languageId": "openqasm"
        }
      },
      "outputs": [],
      "source": [
        "%%openqasm --name bell\n",
        "include \"stdgates.inc\";\n",
        "qubit[2] q;\n",
        "h q[0];\n",
        "cx q[0], q[1];\n",
        "qit[2] c;\n",
        "c[0] = measure q[0];\n",
        "c[1] = measure q[1];"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a177ac9b",
      "metadata": {},
      "source": [
        "With the OpenQASM program loaded into a callable name `bell`, we can now import it via the QDK's Python bindings:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "828cdc9e",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qsharp.code import bell\n",
        "bell()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "11f805c6",
      "metadata": {},
      "source": [
        "Additionally, since it is defined in the session, we can run it directly from a Q# cell:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9baddcfa",
      "metadata": {
        "vscode": {
          "languageId": "qsharp"
        }
      },
      "outputs": [],
      "source": [
        "%%qsharp\n",
        "c"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "db62e61f",
      "metadata": {},
      "source": [
        "This also unlocks all of the other `qsharp` package functionality. Like noisy simulation:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a2ee4980",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qsharp_widgets import Histogram\n",
        "\n",
        "Histogram(qsharp.run(\"bell()\", shots=1000, noise=qsharp.DepolarizingNoise(0.01)))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b1a49542",
      "metadata": {},
      "source": [
        "### Circuit Rendering (textual)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "de7ec5f2",
      "metadata": {},
      "outputs": [],
      "source": [
        "qsharp.circuit(qsharp.code.bell)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1eb60c84",
      "metadata": {},
      "source": [
        "### Circuit Rendering (widget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1ae21fe1",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qsharp_widgets import Circuit\n",
        "Circuit(qsharp.circuit(qsharp.code.bell))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "96446087",
      "metadata": {},
      "source": [
        "### Compilation\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "fdfcab02",
      "metadata": {},
      "outputs": [],
      "source": [
        "print(qsharp.compile(bell))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "62e33a40",
      "metadata": {},
      "source": [
        "#### QIR generation semantic errors\n",
        "\n",
        "When targetting harware by compiling to QIR there are additional restrictions which may cause compilation errors. Most common scenarios:\n",
        "- Trying to generate QIR when the profile is set to `Unrestricted`. `Unrestricted` is only valid for simulation. Either `TargetProfile.Base` or `TargetProfile.Adaptive_RI` must be used.\n",
        "- Not all bits in classical registers have been assigned to. Usually because there were no measurements, or extra registers were declared.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac63ad88",
      "metadata": {},
      "source": [
        "Example, generating QIR with `Unrestricted`"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "10d7dcb0",
      "metadata": {
        "vscode": {
          "languageId": "openqasm"
        }
      },
      "outputs": [],
      "source": [
        "%%openqasm\n",
        "include \"stdgates.inc\";\n",
        "qubit[3] q;\n",
        "h q[1];\n",
        "cx q[1], q[2];\n",
        "cx q[0], q[1];\n",
        "h q[0];\n",
        "bit[2] c;\n",
        "c[0] = measure q[0];\n",
        "c[1] = measure q[1];\n",
        "if (c[0] == 1) z q[2];\n",
        "if (c[1] == 1) x q[2];\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fa219de1",
      "metadata": {},
      "source": [
        "To avoid this issue, set the `target_profile` argument either in the `QSharpBackend` creation or in the `backend.qir` call."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3170569e",
      "metadata": {},
      "source": [
        "When generating `QIR`, all output registers must be read into before generating QIR. Failure to do so results in a `QSharpError`.\n",
        "\n",
        "In this next example, we declare two output bits, but only measure into one. This causes an error because result values can only be a side effect of measurement, and cannot be used like classical variables when compiling for hardware."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "071480db",
      "metadata": {},
      "outputs": [],
      "source": [
        "circuit = QuantumCircuit(2, 2)\n",
        "circuit.x(0)\n",
        "circuit.measure(0, 1)\n",
        "backend = QSharpBackend(target_profile=TargetProfile.Base)\n",
        "try:\n",
        "    print(backend.qir(circuit))\n",
        "except QSharpError as ex:\n",
        "    print(ex)"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": ".venv",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.13.3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
