{
 "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": "e8f7b9102d4d4a8b8b14c404af5a191e",
       "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 syntheisize 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": "d83baba969474c70940287978e39abe0",
       "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": "90a8e20a35fa4591be278aa0a7982861",
       "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": "8790540b2821494f82beea3fc5496a23",
       "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": "bc7c91299732407999d10edf4e316715",
       "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": [
      "result was One, applying X gate\n",
      "result was Zero\n",
      "result was One, applying X gate\n"
     ]
    },
    {
     "ename": "QSharpError",
     "evalue": "Error: Result comparison is unsupported for this backend\nCall 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[0mResult comparison is unsupported for this backend\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",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mQSharpError\u001b[0m                               Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[16], line 5\u001b[0m\n\u001b[0;32m      2\u001b[0m qsharp\u001b[38;5;241m.\u001b[39mrun(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mResetIfOne()\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;241m3\u001b[39m)\n\u001b[0;32m      4\u001b[0m \u001b[38;5;66;03m# The same program cannot be synthesized as a circuit because of the conditional X gate.\u001b[39;00m\n\u001b[1;32m----> 5\u001b[0m qsharp\u001b[38;5;241m.\u001b[39mcircuit(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mResetIfOne()\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
      "File \u001b[1;32mc:\\src\\qsharp\\.venv\\Lib\\site-packages\\qsharp\\_qsharp.py:259\u001b[0m, in \u001b[0;36mcircuit\u001b[1;34m(entry_expr, operation)\u001b[0m\n\u001b[0;32m    244\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcircuit\u001b[39m(\n\u001b[0;32m    245\u001b[0m     entry_expr: Optional[\u001b[38;5;28mstr\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m, operation: Optional[\u001b[38;5;28mstr\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m    246\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Circuit:\n\u001b[0;32m    247\u001b[0m \u001b[38;5;250m    \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m    248\u001b[0m \u001b[38;5;124;03m    Synthesizes a circuit for a Q# program. Either an entry\u001b[39;00m\n\u001b[0;32m    249\u001b[0m \u001b[38;5;124;03m    expression or an operation must be provided.\u001b[39;00m\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    257\u001b[0m \u001b[38;5;124;03m    :raises QSharpError: If there is an error synthesizing the circuit.\u001b[39;00m\n\u001b[0;32m    258\u001b[0m \u001b[38;5;124;03m    \"\"\"\u001b[39;00m\n\u001b[1;32m--> 259\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m get_interpreter()\u001b[38;5;241m.\u001b[39mcircuit(entry_expr, operation)\n",
      "\u001b[1;31mQSharpError\u001b[0m: Error: Result comparison is unsupported for this backend\nCall 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[0mResult comparison is unsupported for this backend\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"
     ]
    }
   ],
   "source": [
    "# Program can be simulated. Differerent shots may produce different results.\n",
    "qsharp.run(\"ResetIfOne()\", 3)\n",
    "\n",
    "# The same program cannot be synthesized as a circuit because of the conditional X gate.\n",
    "qsharp.circuit(\"ResetIfOne()\")"
   ]
  },
  {
   "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": "c36cf04725e0491aa72a621f0d44f933",
       "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.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
