{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "ae56fce0",
      "metadata": {},
      "source": [
        "# QDK Interop with OpenQASM"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2b838c23",
      "metadata": {},
      "source": [
        "The QDK provides interoperability with OpenQASM 3 programs built upon the core QDK 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 OpenQASM sample notebook](../estimation/estimation-openqasm.ipynb)), leveraging the QDK compiler's capabilities for analysis, transformation, code generation, and simulation. This also enables the generation of QIR from OpenQASM progams leveraging the [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.\n",
        "\n",
        "### Simulating OpenQASM programs"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "016b3815",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qsharp.openqasm import run\n",
        "\n",
        "source = \"\"\"\n",
        "    include \"stdgates.inc\";\n",
        "    bit[2] c;\n",
        "    qubit[2] q;\n",
        "    h q[0];\n",
        "    cx q[0], q[1];\n",
        "    c = measure q;\n",
        "\"\"\"\n",
        "\n",
        "# We'll pass as_bitstring=True to convert bit[n] to a bitstring in the output.\n",
        "# Otherwise, the output would be a list of Result values.\n",
        "run(source, as_bitstring=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "04429707",
      "metadata": {},
      "source": [
        "The OpenQASM programs can also be run with noise just as with Q#."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "dc017684",
      "metadata": {},
      "outputs": [],
      "source": [
        "import qsharp\n",
        "from qsharp import DepolarizingNoise\n",
        "from qsharp.openqasm import run\n",
        "from qsharp_widgets import Histogram\n",
        "\n",
        "source = \"\"\"\n",
        "    include \"stdgates.inc\";\n",
        "    bit[2] c;\n",
        "    qubit[2] q;\n",
        "    h q[0];\n",
        "    cx q[0], q[1];\n",
        "    c = measure q;\n",
        "\"\"\"\n",
        "\n",
        "Histogram(run(source, noise=DepolarizingNoise(0.01), as_bitstring=True))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "981d489a",
      "metadata": {},
      "source": [
        "### Compiling OpenQASM to Quantum Intermediate Representation (QIR)\n",
        "\n",
        "We can directly compile OpenQASM to QIR with the `compile` function."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "fc71f209",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qsharp.openqasm import compile\n",
        "\n",
        "source = \"\"\"\n",
        "    include \"stdgates.inc\";\n",
        "    bit[2] c;\n",
        "    qubit[2] q;\n",
        "    h q[0];\n",
        "    cx q[0], q[1];\n",
        "    c = measure q;\n",
        "\"\"\"\n",
        "\n",
        "compilation = compile(source)\n",
        "\n",
        "print(compilation)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "95d82f22",
      "metadata": {},
      "source": [
        "> For parameterized circuits the `import_openqasm` function must be used to first create a Python callable. A sample parameterized circuit can be found later in this notebook.\n",
        "\n",
        "### Run OpenQASM 3 Code in interactive session\n",
        "\n",
        "Import the `qsharp` module.\n",
        "\n",
        "This initializes a QDK interpreter singleton."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "75b8b81b",
      "metadata": {},
      "outputs": [],
      "source": [
        "import qsharp\n",
        "qsharp.init(target_profile=qsharp.TargetProfile.Base)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5dc25416",
      "metadata": {},
      "source": [
        "With the runtime initialized, we can import an OpenQASM program as a Python callable. Here we'll compile the OpenQASM program to a callable name `\"bell\"`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "012cc902",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qsharp.openqasm import import_openqasm, ProgramType\n",
        "\n",
        "source = \"\"\"\n",
        "    include \"stdgates.inc\";\n",
        "    bit[2] c;\n",
        "    qubit[2] q;\n",
        "    h q[0];\n",
        "    cx q[0], q[1];\n",
        "    c = measure q;\n",
        "\"\"\"\n",
        "\n",
        "import_openqasm(source, name=\"bell\", program_type=ProgramType.File)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b95a0c8b",
      "metadata": {},
      "source": [
        "We can now import it via the QDK's Python bindings and run it:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "db043bda",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qsharp.code.qasm_import import bell\n",
        "bell()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8db074dc",
      "metadata": {},
      "source": [
        "Additionally, since it is defined in the runtime, we can run it directly from a Q# cell:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ad6d0331",
      "metadata": {
        "vscode": {
          "languageId": "qsharp"
        }
      },
      "outputs": [],
      "source": [
        "%%qsharp\n",
        "qasm_import.bell()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6578cadc",
      "metadata": {},
      "source": [
        "This also unlocks all of the other `qsharp` package functionality. Like noisy simulation. Here we'll use the `run` function showing how we can call into the program from Python and display a histogram:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "5bbd6d92",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qsharp import DepolarizingNoise\n",
        "from qsharp.openqasm import run\n",
        "from qsharp_widgets import Histogram\n",
        "\n",
        "Histogram(run(bell, shots=1000, noise=DepolarizingNoise(0.01)))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "85d48772",
      "metadata": {},
      "source": [
        "We can draw the progam as a textual circuit rendering passing the Python callable into the circuit function:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a0a50634",
      "metadata": {},
      "outputs": [],
      "source": [
        "qsharp.circuit(bell)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5865ea0c",
      "metadata": {},
      "source": [
        "In notebooks, we can do a bit better leveraging the circuit widget:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "c3fc87ae",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qsharp_widgets import Circuit\n",
        "\n",
        "Circuit(qsharp.circuit(bell))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e1a29c8",
      "metadata": {},
      "source": [
        "And finally when getting ready to submit to hardware, we can compile the program to QIR:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ec6db1e5",
      "metadata": {},
      "outputs": [],
      "source": [
        "print(qsharp.compile(bell))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bb2ec2c1",
      "metadata": {},
      "source": [
        "Alternatively, an OpenQASM program can be imported as an operation in Q#, where the qubits declared in OpenQASM become arguments passed into the operation. This allows the operation to be called as part of a larger Q# program that may perform additional operations before or after the imported operation."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ec34deca",
      "metadata": {},
      "outputs": [],
      "source": [
        "source = \"\"\"\n",
        "    include \"stdgates.inc\";\n",
        "    bit[2] c;\n",
        "    qubit[2] q;\n",
        "    h q[0];\n",
        "    cx q[0], q[1];\n",
        "    c = measure q;\n",
        "\"\"\"\n",
        "\n",
        "import_openqasm(source, name=\"BellOp\", program_type=ProgramType.Operation)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1161a64d",
      "metadata": {},
      "source": [
        "Here, we call the resulting operation from within a Q# cell by first allocating and preparing the qubits before passing them as arguments, resetting them before they are released at the end of the scope."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "28e8932e",
      "metadata": {
        "vscode": {
          "languageId": "qsharp"
        }
      },
      "outputs": [],
      "source": [
        "%%qsharp\n",
        "{\n",
        "    use qs = Qubit[2];\n",
        "    // Change the state of the second qubit before invoking the operation to show\n",
        "    // an anti-correlation in the results.\n",
        "    X(qs[1]);\n",
        "    let result = BellOp(qs);\n",
        "    ResetAll(qs);\n",
        "    result\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "759f6a5e",
      "metadata": {},
      "source": [
        "We can also define input for the compiled OpenQASM code so that we can parameterize input with imported callables:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "bd179f1a",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qsharp import init, TargetProfile\n",
        "\n",
        "source = \"\"\"\n",
        "include \"stdgates.inc\";\n",
        "input float theta;\n",
        "bit[2] c;\n",
        "qubit[2] q;\n",
        "rx(theta) q[0];\n",
        "rx(-theta) q[1];\n",
        "c = measure q;\n",
        "\"\"\"\n",
        "\n",
        "init(target_profile=TargetProfile.Base)\n",
        "import_openqasm(source, name=\"parameterized_program\", program_type=ProgramType.File)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "5b7a97d2",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qsharp.code.qasm_import import parameterized_program\n",
        "from qsharp.openqasm import compile\n",
        "\n",
        "print(compile(parameterized_program, 1.57))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "31d20b6c",
      "metadata": {},
      "source": [
        "When running an OpenQASM program in simulation with qubit loss, additional `Result.Loss` values may be returned that indicate the measured qubit was lost during execution:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "168c3226",
      "metadata": {},
      "outputs": [],
      "source": [
        "init()\n",
        "\n",
        "source = \"\"\"\n",
        "include \"stdgates.inc\";\n",
        "bit[2] c;\n",
        "qubit[2] q;\n",
        "c = measure q;\n",
        "\"\"\"\n",
        "\n",
        "import_openqasm(source, name=\"measure2\", program_type=ProgramType.File)\n",
        "\n",
        "from qsharp.code.qasm_import import measure2\n",
        "\n",
        "Histogram(run(measure2, shots=1000, qubit_loss=0.1))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4773ac0e",
      "metadata": {},
      "source": [
        "By using the special include `\"qdk.inc\"` you can check for loss at runtime using the `mresetz_checked` function. It returns an integer with two bits to indicate whether or not loss has occurred, such that `0` or `1` correspond to the qubit measurement and `2` corresponds to loss:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "02a97625",
      "metadata": {},
      "outputs": [],
      "source": [
        "source = \"\"\"\n",
        "include \"stdgates.inc\";\n",
        "include \"qdk.inc\";\n",
        "qubit q;\n",
        "output int res;\n",
        "h q;\n",
        "res = mresetz_checked(q);\n",
        "\"\"\"\n",
        "\n",
        "import_openqasm(source, name=\"mresetz_checked_example\", program_type=ProgramType.File)\n",
        "\n",
        "from qsharp.code.qasm_import import mresetz_checked_example\n",
        "\n",
        "Histogram(run(mresetz_checked_example, shots=1000, qubit_loss=0.1))\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "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.11.14"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
