{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Synthesizing circuit diagrams from Q# code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "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": null,
   "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": null,
   "metadata": {},
   "outputs": [],
   "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": null,
   "metadata": {},
   "outputs": [],
   "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": null,
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [],
   "source": [
    "%%qsharp\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": null,
   "metadata": {},
   "outputs": [],
   "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": null,
   "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": null,
   "metadata": {},
   "outputs": [],
   "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": null,
   "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": null,
   "metadata": {},
   "outputs": [],
   "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": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "qsharp.init(target_profile=qsharp.TargetProfile.Base)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "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": null,
   "metadata": {},
   "outputs": [],
   "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": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "qsharp.init(target_profile=qsharp.TargetProfile.Unrestricted)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "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": null,
   "metadata": {},
   "outputs": [],
   "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": null,
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "\n",
    "ResetIfOne()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "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
}
