{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Synthesizing circuit diagrams from Q# code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qdk import qsharp"
   ]
  },
  {
   "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": [
    "qsharp.circuit(\"GHZSample(3)\")"
   ]
  },
  {
   "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 qdk.widgets import Circuit\n",
    "\n",
    "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": [
    "By default, conditionals that compare `Result` values are not permitted during circuit synthesis. This is because the default method traces a single execution path through the program, and a conditional may produce a different circuit depending on measurement outcome."
   ]
  },
  {
   "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": [
    "There are two options for synthesizing circuits for programs that contain measurement-based conditionals.\n",
    "\n",
    "### Option 1: Simulate\n",
    "\n",
    "By passing `generation_method=CircuitGenerationMethod.Simulate`, the program is run in the simulator and the resulting gates are recorded. 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": {},
   "outputs": [],
   "source": [
    "from qdk.qsharp import CircuitGenerationMethod\n",
    "\n",
    "Circuit(qsharp.circuit(\"ResetIfOne()\", generation_method=CircuitGenerationMethod.Simulate))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Option 2: Static\n",
    "\n",
    "By passing `generation_method=CircuitGenerationMethod.Static`, the circuit is generated via static analysis of the program. This produces a circuit that shows _all_ conditional branches, rendered as classically controlled groups. This requires a target profile that supports mid-circuit measurement, such as `Adaptive_RIF`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "qsharp.init(target_profile=qsharp.TargetProfile.Adaptive_RIF)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "qsharp"
    }
   },
   "outputs": [],
   "source": [
    "%%qsharp\n",
    "\n",
    "operation ResetIfOne2() : Result {\n",
    "    use q = Qubit();\n",
    "    H(q);\n",
    "    let r = M(q);\n",
    "    if (r == One) {\n",
    "        X(q);\n",
    "    }\n",
    "    Reset(q);\n",
    "    return r\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Circuit(qsharp.circuit(\"ResetIfOne2()\", generation_method=CircuitGenerationMethod.Static))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
