{
  "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 qdk.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": [
        "from qdk import DepolarizingNoise\n",
        "from qdk.openqasm import run\n",
        "from qdk.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 qdk.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 `init` method from qdk.\n",
        "\n",
        "This initializes a QDK interpreter singleton."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "75b8b81b",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qdk import init, TargetProfile\n",
        "init(target_profile=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 qdk.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 qdk import code\n",
        "bell = code.qasm_import.bell\n",
        "\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 `qdk` module 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 qdk import DepolarizingNoise\n",
        "from qdk.openqasm import run\n",
        "from qdk.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": [
        "from qdk.openqasm import circuit\n",
        "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 qdk.widgets import Circuit\n",
        "\n",
        "Circuit(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": [
        "from qdk.openqasm import compile\n",
        "print(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 qdk 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 qdk import code\n",
        "from qdk.openqasm import compile\n",
        "\n",
        "qasm_import = code.qasm_import\n",
        "parameterized_program = qasm_import.parameterized_program\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 qdk import code\n",
        "measure2 = code.qasm_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",
        "mresetz_checked_example = code.qasm_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.14.3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
