{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Circuit\n",
    "\n",
    "Quara supports quantum circuit simulation using Circuit class. Here we will briefly explain how to design, create, run and obtain results from a qunatum circuit. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from quara.objects.circuit import Circuit\n",
    "from quara.objects.composite_system_typical import generate_composite_system\n",
    "from quara.objects.gate_typical import generate_gate_from_gate_name\n",
    "from quara.objects.state_typical import generate_state_from_name\n",
    "from quara.objects.mprocess_typical import generate_mprocess_from_name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Designing a circuit\n",
    "\n",
    "In this notebook, we will focus on creating and executing a quantum circuit of the following diagram.\n",
    "\n",
    "![circuit](./img/circuit_diagram.png)\n",
    "\n",
    "We will create a circuit object which is a 3 qubit system."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "circuit = Circuit(3, \"qubit\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then, we add gates by specifying `gate_name` just like we create quantum objects by names in Quara.\n",
    "`\"cx\"` means CNOT gate. The first number of the `ids` array in a \"cx\" gate specifies the control qubit and second number specifies the target qubit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Type:\n",
      "Circuit\n",
      "\n",
      "QObjects:\n",
      "[\n",
      "  {'Type': 'Gate', 'TargetIds': [1, 0], 'Name': 'cx'},\n",
      "  {'Type': 'Gate', 'TargetIds': [2], 'Name': 'hadamard'},\n",
      "  {'Type': 'Gate', 'TargetIds': [1, 2], 'Name': 'cx'},\n",
      "]\n"
     ]
    }
   ],
   "source": [
    "circuit.add_gate([1,0], gate_name=\"cx\")\n",
    "circuit.add_gate([2], gate_name=\"hadamard\")\n",
    "circuit.add_gate([1,2], gate_name=\"cx\")\n",
    "\n",
    "# describe circuit\n",
    "print(circuit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The adding process in the previous cell is identical to the following code.\n",
    "\n",
    "```python\n",
    "c_sys_1 = generate_composite_system(\"qubit\", 1)\n",
    "c_sys_2 = generate_composite_system(\"qubit\", 2)\n",
    "\n",
    "hadamard = generate_gate_from_gate_name(\"hadamard\", c_sys_1)\n",
    "cnot = generate_gate_from_gate_name(\"cx\", c_sys_2, ids=[0,1])\n",
    "\n",
    "circuit.add_gate([1,0], cnot)\n",
    "circuit.add_gate([2], hadamard)\n",
    "circuit.add_gate([1,2], cnot)\n",
    "```\n",
    "\n",
    "When you add a gate by specifying `gate_name` in the Circuit object, Quara Circuit will automatically generate multiple qubit gates such as CNOT and toffoli and the target qubits will be determined by the `ids` argument given to `add_gate` function."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we add measuremet operations, also by specifying `mprocess_name` just like generating MProcess objects in Quara."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Type:\n",
      "Circuit\n",
      "\n",
      "QObjects:\n",
      "[\n",
      "  {'Type': 'Gate', 'TargetIds': [1, 0], 'Name': 'cx'},\n",
      "  {'Type': 'Gate', 'TargetIds': [2], 'Name': 'hadamard'},\n",
      "  {'Type': 'Gate', 'TargetIds': [1, 2], 'Name': 'cx'},\n",
      "  {'Type': 'MProcess', 'TargetIds': [0], 'KrausMatrixIndices': [1, 1], 'Name': 'x-type1'},\n",
      "  {'Type': 'MProcess', 'TargetIds': [1], 'KrausMatrixIndices': [1, 1], 'Name': 'y-type1'},\n",
      "  {'Type': 'MProcess', 'TargetIds': [2], 'KrausMatrixIndices': [1, 1], 'Name': 'z-type1'},\n",
      "]\n"
     ]
    }
   ],
   "source": [
    "circuit.add_mprocess([0], mprocess_name=\"x-type1\")\n",
    "circuit.add_mprocess([1], mprocess_name=\"y-type1\")\n",
    "circuit.add_mprocess([2], mprocess_name=\"z-type1\")\n",
    "\n",
    "print(circuit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The process in the previous cell is identical to the following code.\n",
    "\n",
    "```python\n",
    "c_sys_1 = generate_composite_system(\"qubit\", 1)\n",
    "\n",
    "measure_x = generate_mprocess_from_name(c_sys_1, \"x-type1\")\n",
    "measure_y = generate_mprocess_from_name(c_sys_1, \"y-type1\")\n",
    "measure_z = generate_mprocess_from_name(c_sys_1, \"z-type1\")\n",
    "\n",
    "circuit.add_mprocess([0], mprocess=measure_x)\n",
    "circuit.add_mprocess([1], mprocess=measure_y)\n",
    "circuit.add_mprocess([2], mprocess=measure_z)\n",
    "```\n",
    "\n",
    "The index of a target qubit can be specified as the `ids` argument."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we prepare for the circuit execution. We can either create initial states by our states or make the library do it.\n",
    "In this example, we will create initial states of every qubits using Quara's features. \n",
    "Then we call the `run()` function by giving the array of states as `initial_states` argument.\n",
    "The first argument of `run()` (which is `num_shots`) specifies the number of execution of the circuit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Type:\n",
      "CircuitResult\n",
      "\n",
      "Circuit Overview:\n",
      "[\n",
      "  {'Type': 'Gate', 'TargetIds': [1, 0], 'Name': 'cx'},\n",
      "  {'Type': 'Gate', 'TargetIds': [2], 'Name': 'hadamard'},\n",
      "  {'Type': 'Gate', 'TargetIds': [1, 2], 'Name': 'cx'},\n",
      "  {'Type': 'MProcess', 'TargetIds': [0], 'KrausMatrixIndices': [1, 1], 'Name': 'x-type1'},\n",
      "  {'Type': 'MProcess', 'TargetIds': [1], 'KrausMatrixIndices': [1, 1], 'Name': 'y-type1'},\n",
      "  {'Type': 'MProcess', 'TargetIds': [2], 'KrausMatrixIndices': [1, 1], 'Name': 'z-type1'},\n",
      "]\n",
      "\n",
      "Empirical distributions:\n",
      "[\n",
      "  [0.5 0.5],\n",
      "  [0.523 0.477],\n",
      "  [0.482 0.518],\n",
      "]\n"
     ]
    }
   ],
   "source": [
    "c_sys_1 = generate_composite_system(\"qubit\", 1)\n",
    "\n",
    "qubit_0 = generate_state_from_name(c_sys_1, \"z0\")\n",
    "qubit_1 = generate_state_from_name(c_sys_1, \"z0\")\n",
    "qubit_2 = generate_state_from_name(c_sys_1, \"z0\")\n",
    "initial_states = [qubit_0, qubit_1, qubit_2]\n",
    "\n",
    "result = circuit.run(1000,initial_states=initial_states)\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can generate initial states automaticaly by specifying `initial_state_name=\"all_zero\"`\n",
    "\n",
    "```python\n",
    "result = circuit.run(1000,initial_state_name=\"all_zero\")\n",
    "```\n",
    "\n",
    "Which is identical to the following.\n",
    "\n",
    "```python\n",
    "c_sys_1 = generate_composite_system(\"qubit\", 1)\n",
    "initial_states = [generate_state_from_name(c_sys_1, \"z0\") for i in range(3)]\n",
    "result = circuit.run(1000,initial_states=initial_states)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's look inside the results.\n",
    "`CircuitResult` class has 2 properties, `raw_result` and `empi_dists`. `raw_result` is the raw outcome data of MProcess objects implemented inside of Circuit. `empi_dists` is a List of empirical distributions which is calculated from raw outcome data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "length of raw_result: 1000\n",
      "[0, 0, 0]\n",
      "length of empi_dists: 3\n",
      "shape = (2,)\n",
      "ps = [0.5 0.5]\n"
     ]
    }
   ],
   "source": [
    "# raw_result\n",
    "print(f\"length of raw_result: {len(result.raw_result)}\") # matches to num_shots\n",
    "print(result.raw_result[0])\n",
    "\n",
    "# empi_dists\n",
    "print(f\"length of empi_dists: {len(result.empi_dists)}\") # matches to the nubmer of MProcess inside of circuit\n",
    "print(result.empi_dists[0])   # the statistical result of first MProcess 'x-type1'"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "eed905c615086ea72e3063d4209dc24edd03b35d7bed26d422a9d986df5b33b5"
  },
  "kernelspec": {
   "display_name": "Python 3.8.7 ('venv': 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.8.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
