{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Synthesizing circuit diagrams from Q# code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "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"
    }
   ],
   "source": [
    "import qsharp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `dump_circuit()` function displays a circuit that contains the gates that have been applied in the simulator up to this point."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "\n",
    "// Prepare a Bell State.\n",
    "use register = Qubit[2];\n",
    "H(register[0]);\n",
    "CNOT(register[0], register[1]);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "q_0    ── H ──── ● ──\n",
       "q_1    ───────── X ──"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "qsharp.dump_circuit()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you have the Q# widgets installed, you can display the circuit as an SVG image.\n",
    "\n",
    "_Run `pip install qsharp-widgets` in the command line to install the Q# widgets._"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "4a760784b95c464cbeeb20e6cb39493f",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Circuit(circuit_json='{\"operations\":[{\"gate\":\"H\",\"targets\":[{\"qId\":0,\"type\":0}]},{\"gate\":\"X\",\"isControlled\":tr…"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from qsharp_widgets import Circuit\n",
    "\n",
    "Circuit(qsharp.dump_circuit())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can synthesize a circuit diagram for any program by calling `qsharp.circuit()` with an entry expression."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "\n",
    "open Microsoft.Quantum.Diagnostics;\n",
    "open Microsoft.Quantum.Measurement;\n",
    "\n",
    "operation GHZSample(n: Int) : Result[] {\n",
    "    use qs = Qubit[n];\n",
    "\n",
    "    H(qs[0]);\n",
    "    ApplyToEach(CNOT(qs[0], _), qs[1...]);\n",
    "\n",
    "    let results = MeasureEachZ(qs);\n",
    "    ResetAll(qs);\n",
    "    return results;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "739e08c87275454186529bcd76156a0d",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Circuit(circuit_json='{\"operations\":[{\"gate\":\"H\",\"targets\":[{\"qId\":0,\"type\":0}]},{\"gate\":\"X\",\"isControlled\":tr…"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Circuit(qsharp.circuit(\"GHZSample(3)\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Circuit diagrams can also be generated for any operation that takes qubits or arrays of qubits.\n",
    "\n",
    "The diagram will show as many wires as there are input qubit, plus any additional qubits that are allocated within the operation.\n",
    "\n",
    "When the operation takes an array of qubits (`Qubit[]`), the circuit will show the array as a register of 2 qubits."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "\n",
    "operation PrepareCatState(register : Qubit[]) : Unit {\n",
    "    H(register[0]);\n",
    "    ApplyToEach(CNOT(register[0], _), register[1...]);\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "17f2fea978c24ccb8ae137888ac4aa05",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Circuit(circuit_json='{\"operations\":[{\"gate\":\"H\",\"targets\":[{\"qId\":0,\"type\":0}]},{\"gate\":\"X\",\"isControlled\":tr…"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Circuit(qsharp.circuit(operation=\"PrepareCatState\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Circuit synthesis takes into account the currently chosen target, and will perform the same gate decompositions and other transformations that compiling for that target would produce.\n",
    "\n",
    "Here, we show what the circuit looks like for a random bit generator when the Unrestricted target profile is chosen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "\n",
    "operation TwoRandomBits() : Result[] {\n",
    "    let r1 = RandomBit();\n",
    "    let r2 = RandomBit();\n",
    "    return [r1, r2];\n",
    "}\n",
    "\n",
    "operation RandomBit() : Result {\n",
    "    use q = Qubit();\n",
    "    H(q);\n",
    "    MResetZ(q)\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "2dac021b8bf449b5a8df9fef0894f7b2",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Circuit(circuit_json='{\"operations\":[{\"gate\":\"H\",\"targets\":[{\"qId\":0,\"type\":0}]},{\"gate\":\"Measure\",\"isMeasurem…"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Circuit(qsharp.circuit(\"TwoRandomBits()\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we generate a circuit for the same program, but targeting the Base profile, the resulting circuit avoids reset gates and uses two qubits instead."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/x.qsharp-config": "{\"targetProfile\":\"base\",\"languageFeatures\":[]}",
      "text/plain": [
       "Q# initialized with configuration: {'targetProfile': 'base', 'languageFeatures': []}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "qsharp.init(target_profile=qsharp.TargetProfile.Base)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "\n",
    "operation TwoRandomBits() : Result[] {\n",
    "    let r1 = RandomBit();\n",
    "    let r2 = RandomBit();\n",
    "    return [r1, r2];\n",
    "}\n",
    "\n",
    "operation RandomBit() : Result {\n",
    "    use q = Qubit();\n",
    "    H(q);\n",
    "    MResetZ(q)\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "df689aa58fa842ccb668294f5ce1e56b",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Circuit(circuit_json='{\"operations\":[{\"gate\":\"H\",\"targets\":[{\"qId\":0,\"type\":0}]},{\"gate\":\"H\",\"targets\":[{\"qId\"…"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Circuit(qsharp.circuit(\"TwoRandomBits()\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Regardless of the target chosen, conditionals that compare `Result` values are not permitted during circuit synthesis. This is because they may introduce nondeterminism such that the circuit will look different depending on measurement outcome. Representing conditionals visually is not supported."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/x.qsharp-config": "{\"targetProfile\":\"unrestricted\",\"languageFeatures\":[]}",
      "text/plain": [
       "Q# initialized with configuration: {'targetProfile': 'unrestricted', 'languageFeatures': []}"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "qsharp.init(target_profile=qsharp.TargetProfile.Unrestricted)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "\n",
    "operation ResetIfOne() : Result {\n",
    "    use q = Qubit();\n",
    "    H(q);\n",
    "    let r = M(q);\n",
    "    if (r == One) {\n",
    "        Message(\"result was One, applying X gate\");\n",
    "        X(q);\n",
    "    } else {\n",
    "        Message(\"result was Zero\");\n",
    "    }\n",
    "    Reset(q);\n",
    "    return r\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Simulating program...\n",
      "result was Zero\n",
      "result was Zero\n",
      "result was Zero\n",
      "\n",
      "Synthesizing circuit for program (should raise error)...\n",
      "Error: cannot compare measurement results\n",
      "Call stack:\n",
      "    at ResetIfOne in line_0\n",
      "\u001b[31mQsc.Eval.ResultComparisonUnsupported\u001b[0m\n",
      "\n",
      "  \u001b[31m×\u001b[0m runtime error\n",
      "\u001b[31m  ╰─▶ \u001b[0mcannot compare measurement results\n",
      "   ╭─[\u001b[36;1;4mline_0\u001b[0m:5:1]\n",
      " \u001b[2m5\u001b[0m │     let r = M(q);\n",
      " \u001b[2m6\u001b[0m │     if (r == One) {\n",
      "   · \u001b[35;1m             ─┬─\u001b[0m\n",
      "   ·               \u001b[35;1m╰── \u001b[35;1mcannot compare to result\u001b[0m\u001b[0m\n",
      " \u001b[2m7\u001b[0m │         Message(\"result was One, applying X gate\");\n",
      "   ╰────\n",
      "\u001b[36m  help: \u001b[0mcomparing measurement results is not supported when performing\n",
      "        circuit synthesis or base profile QIR generation\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Program can be simulated. Differerent shots may produce different results.\n",
    "print(\"Simulating program...\")\n",
    "qsharp.run(\"ResetIfOne()\", 3)\n",
    "\n",
    "print()\n",
    "\n",
    "# The same program cannot be synthesized as a circuit because of the conditional X gate.\n",
    "print(\"Synthesizing circuit for program (should raise error)...\")\n",
    "try:\n",
    "    qsharp.circuit(\"ResetIfOne()\")\n",
    "except qsharp.QSharpError as e:\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Even though we can't synthesize the above program into a circuit, we still have the option of running it in the simulator, and displaying the resulting circuit.\n",
    "\n",
    "Note that the resulting circuit diagram shows only one of the two branches that could have been taken."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<p>result was Zero</p>"
      ],
      "text/plain": [
       "result was Zero"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "Zero"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%qsharp\n",
    "\n",
    "ResetIfOne()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "c4ef7327e1194d37991f5b46b35f3a9d",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Circuit(circuit_json='{\"operations\":[{\"gate\":\"H\",\"targets\":[{\"qId\":0,\"type\":0}]},{\"gate\":\"Measure\",\"isMeasurem…"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Circuit(qsharp.dump_circuit())"
   ]
  }
 ],
 "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.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
